bim.c 368 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452
  1. /**
  2. * This is a baked, single-file version of bim.
  3. * It was built Thu Feb 27 21:56:23 2020
  4. * It is based on git commit 83e6cc609584bd31e961c3873a9f3a5c7c2973ec
  5. */
  6. #define GIT_TAG "83e6cc6-baked"
  7. /* Bim - A Text Editor
  8. *
  9. * Copyright (C) 2012-2020 K. Lange
  10. *
  11. * Permission to use, copy, modify, and/or distribute this software for any
  12. * purpose with or without fee is hereby granted, provided that the above
  13. * copyright notice and this permission notice appear in all copies.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  16. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  17. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  18. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  19. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  20. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  21. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22. */
  23. /* Included from bim-core.h */
  24. #define _XOPEN_SOURCE 700
  25. #define _DARWIN_C_SOURCE
  26. #define _DEFAULT_SOURCE
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <stdint.h>
  31. #include <stdarg.h>
  32. #include <unistd.h>
  33. #include <termios.h>
  34. #include <signal.h>
  35. #include <locale.h>
  36. #include <wchar.h>
  37. #include <ctype.h>
  38. #include <dirent.h>
  39. #include <poll.h>
  40. #include <limits.h>
  41. #include <sys/types.h>
  42. #include <sys/ioctl.h>
  43. #include <sys/stat.h>
  44. #ifdef __TIMESTAMP__
  45. # define BIM_BUILD_DATE " built " __TIMESTAMP__
  46. #else
  47. # define BIM_BUILD_DATE DATE ""
  48. #endif
  49. #ifdef GIT_TAG
  50. # define TAG "-" GIT_TAG
  51. #else
  52. # define TAG ""
  53. #endif
  54. #define BIM_VERSION "2.6.1" TAG
  55. #define BIM_COPYRIGHT "Copyright 2012-2020 K. Lange <\033[3mklange@toaruos.org\033[23m>"
  56. #define BLOCK_SIZE 4096
  57. #define ENTER_KEY '\r'
  58. #define LINE_FEED '\n'
  59. #define BACKSPACE_KEY 0x08
  60. #define DELETE_KEY 0x7F
  61. enum Key {
  62. KEY_TIMEOUT = -1,
  63. KEY_CTRL_AT = 0, /* Base */
  64. KEY_CTRL_A, KEY_CTRL_B, KEY_CTRL_C, KEY_CTRL_D, KEY_CTRL_E, KEY_CTRL_F, KEY_CTRL_G, KEY_CTRL_H,
  65. KEY_CTRL_I, KEY_CTRL_J, KEY_CTRL_K, KEY_CTRL_L, KEY_CTRL_M, KEY_CTRL_N, KEY_CTRL_O, KEY_CTRL_P,
  66. KEY_CTRL_Q, KEY_CTRL_R, KEY_CTRL_S, KEY_CTRL_T, KEY_CTRL_U, KEY_CTRL_V, KEY_CTRL_W, KEY_CTRL_X,
  67. KEY_CTRL_Y, KEY_CTRL_Z, /* Note we keep ctrl-z mapped in termios as suspend */
  68. KEY_CTRL_OPEN, KEY_CTRL_BACKSLASH, KEY_CTRL_CLOSE, KEY_CTRL_CARAT, KEY_CTRL_UNDERSCORE,
  69. /* Space... */
  70. /* Some of these are equivalent to things above */
  71. KEY_BACKSPACE = 0x08,
  72. KEY_LINEFEED = '\n',
  73. KEY_ENTER = '\r',
  74. KEY_TAB = '\t',
  75. /* Basic printable characters go here. */
  76. /* Delete is special */
  77. KEY_DELETE = 0x7F,
  78. /* Unicode codepoints go here */
  79. KEY_ESCAPE = 0x400000, /* Escape would normally be 27, but is special because reasons */
  80. KEY_F1, KEY_F2, KEY_F3, KEY_F4,
  81. KEY_F5, KEY_F6, KEY_F7, KEY_F8,
  82. KEY_F9, KEY_F10, KEY_F11, KEY_F12,
  83. /* TODO ALT, SHIFT, etc., for F keys */
  84. KEY_MOUSE, /* Must be followed with a 3-byte mouse read */
  85. KEY_MOUSE_SGR, /* Followed by an SGR-style sequence of mouse data */
  86. KEY_HOME, KEY_END, KEY_PAGE_UP, KEY_PAGE_DOWN,
  87. KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT,
  88. KEY_SHIFT_UP, KEY_SHIFT_DOWN, KEY_SHIFT_RIGHT, KEY_SHIFT_LEFT,
  89. KEY_CTRL_UP, KEY_CTRL_DOWN, KEY_CTRL_RIGHT, KEY_CTRL_LEFT,
  90. KEY_ALT_UP, KEY_ALT_DOWN, KEY_ALT_RIGHT, KEY_ALT_LEFT,
  91. KEY_ALT_SHIFT_UP, KEY_ALT_SHIFT_DOWN, KEY_ALT_SHIFT_RIGHT, KEY_ALT_SHIFT_LEFT,
  92. KEY_SHIFT_TAB,
  93. /* Special signals for paste start, paste end */
  94. KEY_PASTE_BEGIN, KEY_PASTE_END,
  95. };
  96. struct key_name_map {
  97. enum Key keycode;
  98. char * name;
  99. };
  100. extern struct key_name_map KeyNames[];
  101. /**
  102. * Syntax highlighting flags.
  103. */
  104. #define FLAG_NONE 0
  105. #define FLAG_KEYWORD 1
  106. #define FLAG_STRING 2
  107. #define FLAG_COMMENT 3
  108. #define FLAG_TYPE 4
  109. #define FLAG_PRAGMA 5
  110. #define FLAG_NUMERAL 6
  111. #define FLAG_ERROR 7
  112. #define FLAG_DIFFPLUS 8
  113. #define FLAG_DIFFMINUS 9
  114. #define FLAG_NOTICE 10
  115. #define FLAG_BOLD 11
  116. #define FLAG_LINK 12
  117. #define FLAG_ESCAPE 13
  118. #define FLAG_SELECT (1 << 5)
  119. #define FLAG_SEARCH (1 << 6)
  120. /**
  121. * Line buffer definitions
  122. *
  123. * Lines are essentially resizable vectors of char_t structs,
  124. * which represent single codepoints in the file.
  125. */
  126. typedef struct {
  127. uint32_t display_width:4;
  128. uint32_t flags:7;
  129. uint32_t codepoint:21;
  130. } __attribute__((packed)) char_t;
  131. /**
  132. * Lines have available and actual lengths, describing
  133. * how much space was allocated vs. how much is being
  134. * used at the moment.
  135. */
  136. typedef struct {
  137. int available;
  138. int actual;
  139. int istate;
  140. int is_current;
  141. int rev_status;
  142. char_t text[];
  143. } line_t;
  144. /**
  145. * Global configuration state
  146. *
  147. * At the moment, this is all in a global, but in the future
  148. * this should be passed around to various functions.
  149. */
  150. typedef struct {
  151. /* Terminal size */
  152. int term_width, term_height;
  153. int bottom_size;
  154. line_t ** yanks;
  155. size_t yank_count;
  156. int yank_is_full_lines;
  157. int tty_in;
  158. const char * bimrc_path;
  159. const char * syntax_fallback;
  160. uint32_t * search;
  161. int overlay_mode;
  162. line_t * command_buffer;
  163. int command_offset, command_col_no;
  164. struct syntax_definition * command_syn, * command_syn_back;
  165. int history_point;
  166. int search_direction;
  167. int prev_line, prev_col, prev_coffset, prev_offset;
  168. unsigned int highlight_on_open:1;
  169. unsigned int initial_file_is_read_only:1;
  170. unsigned int go_to_line:1;
  171. unsigned int break_from_selection:1;
  172. unsigned int can_scroll:1;
  173. unsigned int can_hideshow:1;
  174. unsigned int can_altscreen:1;
  175. unsigned int can_mouse:1;
  176. unsigned int can_unicode:1;
  177. unsigned int can_bright:1;
  178. unsigned int can_title:1;
  179. unsigned int can_bce:1;
  180. unsigned int can_24bit:1;
  181. unsigned int can_256color:1;
  182. unsigned int can_italic:1;
  183. unsigned int can_insert:1;
  184. unsigned int can_bracketedpaste:1;
  185. unsigned int history_enabled:1;
  186. unsigned int highlight_parens:1;
  187. unsigned int smart_case:1;
  188. unsigned int highlight_current_line:1;
  189. unsigned int shift_scrolling:1;
  190. unsigned int check_git:1;
  191. unsigned int color_gutter:1;
  192. unsigned int relative_lines:1;
  193. unsigned int numbers:1;
  194. unsigned int horizontal_shift_scrolling:1;
  195. unsigned int hide_statusbar:1;
  196. unsigned int tabs_visible:1;
  197. unsigned int autohide_tabs:1;
  198. unsigned int smart_complete:1;
  199. unsigned int has_terminal:1;
  200. unsigned int use_sgr_mouse:1;
  201. unsigned int search_wraps:1;
  202. int cursor_padding;
  203. int split_percent;
  204. int scroll_amount;
  205. int tab_offset;
  206. char * tab_indicator;
  207. char * space_indicator;
  208. } global_config_t;
  209. #define OVERLAY_MODE_NONE 0
  210. #define OVERLAY_MODE_READ_ONE 1
  211. #define OVERLAY_MODE_COMMAND 2
  212. #define OVERLAY_MODE_SEARCH 3
  213. #define OVERLAY_MODE_COMPLETE 4
  214. #define HISTORY_SENTINEL 0
  215. #define HISTORY_INSERT 1
  216. #define HISTORY_DELETE 2
  217. #define HISTORY_REPLACE 3
  218. #define HISTORY_REMOVE_LINE 4
  219. #define HISTORY_ADD_LINE 5
  220. #define HISTORY_REPLACE_LINE 6
  221. #define HISTORY_MERGE_LINES 7
  222. #define HISTORY_SPLIT_LINE 8
  223. #define HISTORY_BREAK 10
  224. typedef struct history {
  225. struct history * previous;
  226. struct history * next;
  227. int type;
  228. int line;
  229. int col;
  230. union {
  231. struct {
  232. int lineno;
  233. int offset;
  234. int codepoint;
  235. int old_codepoint;
  236. } insert_delete_replace;
  237. struct {
  238. int lineno;
  239. line_t * contents;
  240. line_t * old_contents;
  241. } remove_replace_line;
  242. struct {
  243. int lineno;
  244. int split;
  245. } add_merge_split_lines;
  246. } contents;
  247. } history_t;
  248. /**
  249. * Buffer data
  250. *
  251. * A buffer describes a file, and stores
  252. * its name as well as the editor state
  253. * (cursor offsets, etc.) and the actual
  254. * line buffers.
  255. */
  256. typedef struct _env {
  257. unsigned int loading:1;
  258. unsigned int tabs:1;
  259. unsigned int modified:1;
  260. unsigned int readonly:1;
  261. unsigned int indent:1;
  262. unsigned int checkgitstatusonwrite:1;
  263. unsigned int crnl:1;
  264. unsigned int numbers:1;
  265. unsigned int gutter:1;
  266. int highlighting_paren;
  267. int maxcolumn;
  268. short mode;
  269. short tabstop;
  270. char * file_name;
  271. int offset;
  272. int coffset;
  273. int line_no;
  274. int line_count;
  275. int line_avail;
  276. int col_no;
  277. int preferred_column;
  278. struct syntax_definition * syntax;
  279. line_t ** lines;
  280. history_t * history;
  281. history_t * last_save_history;
  282. int width;
  283. int left;
  284. int start_line;
  285. int sel_col;
  286. int start_col;
  287. int prev_line;
  288. } buffer_t;
  289. struct theme_def {
  290. const char * name;
  291. void (*load)(const char * name);
  292. };
  293. extern struct theme_def * themes;
  294. extern void add_colorscheme(struct theme_def theme);
  295. struct syntax_state {
  296. line_t * line;
  297. int line_no;
  298. int state;
  299. int i;
  300. };
  301. struct completion_match {
  302. char * string;
  303. char * file;
  304. char * search;
  305. };
  306. struct syntax_definition {
  307. char * name;
  308. char ** ext;
  309. int (*calculate)(struct syntax_state *);
  310. int prefers_spaces;
  311. int (*completion_qualifier)(int c);
  312. int (*completion_matcher)(uint32_t * comp, struct completion_match ** matches, int * matches_count, int complete_match, int * matches_len);
  313. };
  314. extern struct syntax_definition * syntaxes;
  315. /**
  316. * Editor mode states
  317. */
  318. #define MODE_NORMAL 0
  319. #define MODE_INSERT 1
  320. #define MODE_LINE_SELECTION 2
  321. #define MODE_REPLACE 3
  322. #define MODE_CHAR_SELECTION 4
  323. #define MODE_COL_SELECTION 5
  324. #define MODE_COL_INSERT 6
  325. #define MODE_DIRECTORY_BROWSE 7
  326. extern global_config_t global_config;
  327. extern const char * COLOR_FG;
  328. extern const char * COLOR_BG;
  329. extern const char * COLOR_ALT_FG;
  330. extern const char * COLOR_ALT_BG;
  331. extern const char * COLOR_NUMBER_FG;
  332. extern const char * COLOR_NUMBER_BG;
  333. extern const char * COLOR_STATUS_FG;
  334. extern const char * COLOR_STATUS_BG;
  335. extern const char * COLOR_STATUS_ALT;
  336. extern const char * COLOR_TABBAR_BG;
  337. extern const char * COLOR_TAB_BG;
  338. extern const char * COLOR_ERROR_FG;
  339. extern const char * COLOR_ERROR_BG;
  340. extern const char * COLOR_SEARCH_FG;
  341. extern const char * COLOR_SEARCH_BG;
  342. extern const char * COLOR_KEYWORD;
  343. extern const char * COLOR_STRING;
  344. extern const char * COLOR_COMMENT;
  345. extern const char * COLOR_TYPE;
  346. extern const char * COLOR_PRAGMA;
  347. extern const char * COLOR_NUMERAL;
  348. extern const char * COLOR_SELECTFG;
  349. extern const char * COLOR_SELECTBG;
  350. extern const char * COLOR_RED;
  351. extern const char * COLOR_GREEN;
  352. extern const char * COLOR_BOLD;
  353. extern const char * COLOR_LINK;
  354. extern const char * COLOR_ESCAPE;
  355. extern const char * current_theme;
  356. struct action_def {
  357. char * name;
  358. void (*action)();
  359. int options;
  360. const char * description;
  361. };
  362. extern struct action_def * mappable_actions;
  363. #define ARG_IS_INPUT 0x01 /* Takes the key that triggered it as the first argument */
  364. #define ARG_IS_CUSTOM 0x02 /* Takes a custom argument which is specific to the method */
  365. #define ARG_IS_PROMPT 0x04 /* Prompts for an argument. */
  366. #define ACTION_IS_RW 0x08 /* Needs to be able to write. */
  367. #define BIM_ACTION(name, options, description) \
  368. void name (); /* Define the action with unknown arguments */ \
  369. void __attribute__((constructor)) _install_ ## name (void) { \
  370. add_action((struct action_def){#name, name, options, description}); \
  371. } \
  372. void name
  373. struct command_def {
  374. char * name;
  375. int (*command)(char *, int, char * arg[]);
  376. const char * description;
  377. };
  378. #define BIM_COMMAND(cmd_name, cmd_str, description) \
  379. int bim_command_ ## cmd_name (char * cmd, int argc, char * argv[]); \
  380. void __attribute__((constructor)) _install_cmd_ ## cmd_name (void) { \
  381. add_command((struct command_def){cmd_str, bim_command_ ## cmd_name, description}); \
  382. } \
  383. int bim_command_ ## cmd_name (char * cmd __attribute__((unused)), int argc __attribute__((unused)), char * argv[] __attribute__((unused)))
  384. #define BIM_ALIAS(alias, alias_name, cmd_name) \
  385. void __attribute__((constructor)) _install_alias_ ## alias_name (void) { \
  386. add_command((struct command_def){alias, bim_command_ ## cmd_name, "Alias for " #cmd_name}); \
  387. }
  388. #define BIM_PREFIX_COMMAND(cmd_name, cmd_prefix, description) \
  389. int bim_command_ ## cmd_name (char * cmd, int argc, char * argv[]); \
  390. void __attribute__((constructor)) _install_cmd_ ## cmd_name (void) { \
  391. add_prefix_command((struct command_def){cmd_prefix, bim_command_ ## cmd_name, description}); \
  392. } \
  393. int bim_command_ ## cmd_name (char * cmd __attribute__((unused)), int argc __attribute__((unused)), char * argv[] __attribute__((unused)))
  394. extern buffer_t * env;
  395. extern buffer_t * left_buffer;
  396. extern buffer_t * right_buffer;
  397. #define NAV_BUFFER_MAX 10
  398. extern char nav_buf[NAV_BUFFER_MAX+1];
  399. extern int nav_buffer;
  400. extern int buffers_len;
  401. extern int buffers_avail;
  402. extern buffer_t ** buffers;
  403. extern const char * flag_to_color(int _flag);
  404. extern void redraw_line(int x);
  405. extern int git_examine(char * filename);
  406. extern void search_next(void);
  407. extern void set_preferred_column(void);
  408. extern void quit(const char * message);
  409. extern void close_buffer(void);
  410. extern void set_syntax_by_name(const char * name);
  411. extern void rehighlight_search(line_t * line);
  412. extern void try_to_center();
  413. extern int read_one_character(char * message);
  414. extern void bim_unget(int c);
  415. #define bim_getch() bim_getch_timeout(200)
  416. extern int bim_getch_timeout(int timeout);
  417. extern buffer_t * buffer_new(void);
  418. extern FILE * open_biminfo(void);
  419. extern int fetch_from_biminfo(buffer_t * buf);
  420. extern int update_biminfo(buffer_t * buf);
  421. extern buffer_t * buffer_close(buffer_t * buf);
  422. extern int to_eight(uint32_t codepoint, char * out);
  423. extern char * name_from_key(enum Key keycode);
  424. extern void add_action(struct action_def action);
  425. extern void open_file(char * file);
  426. extern void recalculate_selected_lines(void);
  427. extern void add_command(struct command_def command);
  428. extern void add_prefix_command(struct command_def command);
  429. extern void render_command_input_buffer(void);
  430. extern void unhighlight_matching_paren(void);
  431. extern void add_syntax(struct syntax_definition def);
  432. struct ColorName {
  433. const char * name;
  434. const char ** value;
  435. };
  436. extern struct ColorName color_names[];
  437. struct bim_function {
  438. char * command;
  439. struct bim_function * next;
  440. };
  441. extern struct bim_function ** user_functions;
  442. extern int run_function(char * name);
  443. extern int has_function(char * name);
  444. extern void find_matching_paren(int * out_line, int * out_col, int in_col);
  445. extern void render_error(char * message, ...);
  446. extern void pause_for_key(void);
  447. #define add_match(match_string, match_file, match_search) do { \
  448. if (*matches_count == *matches_len) { \
  449. (*matches_len) *= 2; \
  450. *matches = realloc(*matches, sizeof(struct completion_match) * (*matches_len)); \
  451. } \
  452. (*matches)[*matches_count].string = strdup(match_string); \
  453. (*matches)[*matches_count].file = strdup(match_file); \
  454. (*matches)[*matches_count].search = strdup(match_search); \
  455. (*matches_count)++; \
  456. } while (0)
  457. #define add_if_match(name,desc) do { \
  458. int i = 0; \
  459. while (comp[i] && comp[i] == (unsigned char)name[i]) i++; \
  460. if (comp[i] == '\0') { \
  461. add_match(name,desc,""); \
  462. } \
  463. } while (0)
  464. struct action_map {
  465. int key;
  466. void (*method)();
  467. int options;
  468. int arg;
  469. };
  470. #define opt_rep 0x1 /* This action will be repeated */
  471. #define opt_arg 0x2 /* This action will take a specified argument */
  472. #define opt_char 0x4 /* This action will read a character to pass as an argument */
  473. #define opt_nav 0x8 /* This action will consume the nav buffer as its argument */
  474. #define opt_rw 0x10 /* Must not be read-only */
  475. #define opt_norm 0x20 /* Returns to normal mode */
  476. #define opt_byte 0x40 /* Same as opt_char but forces a byte */
  477. struct mode_names {
  478. const char * description;
  479. const char * name;
  480. struct action_map ** mode;
  481. };
  482. extern struct mode_names mode_names[];
  483. /* End of bim-core.h */
  484. /* Included from bim-syntax.h */
  485. #define BIM_SYNTAX(name, spaces) \
  486. __attribute__((constructor)) static void _load_ ## name (void) { \
  487. add_syntax((struct syntax_definition){#name, syn_ ## name ## _ext, syn_ ## name ## _calculate, spaces, NULL, NULL}); \
  488. } \
  489. #define BIM_SYNTAX_EXT(name, spaces, matcher) \
  490. __attribute__((constructor)) static void _load_ ## name (void) { \
  491. add_syntax((struct syntax_definition){#name, syn_ ## name ## _ext, syn_ ## name ## _calculate, spaces, matcher, _match_completions_ ## name}); \
  492. } \
  493. #define BIM_SYNTAX_COMPLETER(name) \
  494. static int _match_completions_ ## name ( \
  495. uint32_t * comp __attribute__((unused)), \
  496. struct completion_match **matches __attribute__((unused)), \
  497. int * matches_count __attribute__((unused)), \
  498. int complete_match __attribute__((unused)), \
  499. int *matches_len __attribute__((unused)))
  500. #define paint(length, flag) do { for (int i = 0; i < (length) && state->i < state->line->actual; i++, state->i++) { state->line->text[state->i].flags = (flag); } } while (0)
  501. #define charat() (state->i < state->line->actual ? state->line->text[(state->i)].codepoint : -1)
  502. #define nextchar() (state->i + 1 < state->line->actual ? state->line->text[(state->i+1)].codepoint : -1)
  503. #define lastchar() (state->i - 1 >= 0 ? state->line->text[(state->i-1)].codepoint : -1)
  504. #define skip() (state->i++)
  505. #define charrel(x) (state->i + (x) < state->line->actual ? state->line->text[(state->i+(x))].codepoint : -1)
  506. extern int find_keywords(struct syntax_state * state, char ** keywords, int flag, int (*keyword_qualifier)(int c));
  507. extern int match_and_paint(struct syntax_state * state, const char * keyword, int flag, int (*keyword_qualifier)(int c));
  508. extern void paint_single_string(struct syntax_state * state);
  509. extern void paint_simple_string(struct syntax_state * state);
  510. extern int common_comment_buzzwords(struct syntax_state * state);
  511. extern int paint_comment(struct syntax_state * state);
  512. extern int match_forward(struct syntax_state * state, char * c);
  513. extern struct syntax_definition * find_syntax_calculator(const char * name);
  514. #define nest(lang, low) \
  515. do { \
  516. state->state = (state->state < 1 ? 0 : state->state - low); \
  517. do { state->state = lang(state); } while (state->state == 0); \
  518. if (state->state == -1) return low; \
  519. return state->state + low; \
  520. } while (0)
  521. /* Some of the C stuff is widely used */
  522. extern int c_keyword_qualifier(int c);
  523. extern int paint_c_numeral(struct syntax_state * state);
  524. extern int paint_c_comment(struct syntax_state * state);
  525. extern void paint_c_char(struct syntax_state * state);
  526. /* Hacky workaround for isdigit not really accepting Unicode stuff */
  527. static __attribute__((used)) int _isdigit(int c) { if (c > 128) return 0; return isdigit(c); }
  528. static __attribute__((used)) int _isxdigit(int c) { if (c > 128) return 0; return isxdigit(c); }
  529. #undef isdigit
  530. #undef isxdigit
  531. #define isdigit(c) _isdigit(c)
  532. #define isxdigit(c) _isxdigit(c)
  533. /* End of bim-syntax.h */
  534. global_config_t global_config = {
  535. /* State */
  536. .term_width = 0,
  537. .term_height = 0,
  538. .bottom_size = 2,
  539. .yanks = NULL,
  540. .yank_count = 0,
  541. .yank_is_full_lines = 0,
  542. .tty_in = STDIN_FILENO,
  543. .bimrc_path = "~/.bimrc",
  544. .syntax_fallback = NULL, /* syntax to fall back to if none other match applies */
  545. .search = NULL,
  546. .overlay_mode = OVERLAY_MODE_NONE,
  547. .command_buffer = NULL,
  548. .command_offset = 0,
  549. .command_col_no = 0,
  550. .history_point = -1,
  551. /* Bitset starts here */
  552. .highlight_on_open = 1,
  553. .initial_file_is_read_only = 0,
  554. .go_to_line = 1,
  555. .break_from_selection = 1,
  556. /* Terminal capabilities */
  557. .can_scroll = 1,
  558. .can_hideshow = 1,
  559. .can_altscreen = 1,
  560. .can_mouse = 1,
  561. .can_unicode = 1,
  562. .can_bright = 1,
  563. .can_title = 1,
  564. .can_bce = 1,
  565. .can_24bit = 1, /* can use 24-bit color */
  566. .can_256color = 1, /* can use 265 colors */
  567. .can_italic = 1, /* can use italics (without inverting) */
  568. .can_insert = 0, /* ^[[L */
  569. .can_bracketedpaste = 0, /* puts escapes before and after pasted stuff */
  570. /* Configuration options */
  571. .history_enabled = 1,
  572. .highlight_parens = 1, /* highlight parens/braces when cursor moves */
  573. .smart_case = 1, /* smart case-sensitivity while searching */
  574. .highlight_current_line = 1,
  575. .shift_scrolling = 1, /* shift rather than moving cursor*/
  576. .check_git = 0,
  577. .color_gutter = 1, /* shows modified lines */
  578. .relative_lines = 0,
  579. .numbers = 1,
  580. .horizontal_shift_scrolling = 0, /* Whether to shift the whole screen when scrolling horizontally */
  581. .hide_statusbar = 0,
  582. .tabs_visible = 1,
  583. .autohide_tabs = 0,
  584. .smart_complete = 0,
  585. .has_terminal = 0,
  586. .use_sgr_mouse = 0,
  587. .search_wraps = 1,
  588. /* Integer config values */
  589. .cursor_padding = 4,
  590. .split_percent = 50,
  591. .scroll_amount = 5,
  592. .tab_offset = 0,
  593. };
  594. struct key_name_map KeyNames[] = {
  595. {KEY_TIMEOUT, "[timeout]"},
  596. {KEY_BACKSPACE, "<backspace>"},
  597. {KEY_ENTER, "<enter>"},
  598. {KEY_ESCAPE, "<escape>"},
  599. {KEY_TAB, "<tab>"},
  600. {' ', "<space>"},
  601. /* These are mostly here for markdown output. */
  602. {'`', "<backtick>"},
  603. {'|', "<pipe>"},
  604. {KEY_DELETE, "<del>"},
  605. {KEY_MOUSE, "<mouse>"},
  606. {KEY_MOUSE_SGR, "<mouse-sgr>"},
  607. {KEY_F1, "<f1>"},{KEY_F2, "<f2>"},{KEY_F3, "<f3>"},{KEY_F4, "<f4>"},
  608. {KEY_F5, "<f5>"},{KEY_F6, "<f6>"},{KEY_F7, "<f7>"},{KEY_F8, "<f8>"},
  609. {KEY_F9, "<f9>"},{KEY_F10, "<f10>"},{KEY_F11, "<f11>"},{KEY_F12, "<f12>"},
  610. {KEY_HOME,"<home>"},{KEY_END,"<end>"},{KEY_PAGE_UP,"<page-up>"},{KEY_PAGE_DOWN,"<page-down>"},
  611. {KEY_UP, "<up>"},{KEY_DOWN, "<down>"},{KEY_RIGHT, "<right>"},{KEY_LEFT, "<left>"},
  612. {KEY_SHIFT_UP, "<shift-up>"},{KEY_SHIFT_DOWN, "<shift-down>"},{KEY_SHIFT_RIGHT, "<shift-right>"},{KEY_SHIFT_LEFT, "<shift-left>"},
  613. {KEY_CTRL_UP, "<ctrl-up>"},{KEY_CTRL_DOWN, "<ctrl-down>"},{KEY_CTRL_RIGHT, "<ctrl-right>"},{KEY_CTRL_LEFT, "<ctrl-left>"},
  614. {KEY_ALT_UP, "<alt-up>"},{KEY_ALT_DOWN, "<alt-down>"},{KEY_ALT_RIGHT, "<alt-right>"},{KEY_ALT_LEFT, "<alt-left>"},
  615. {KEY_ALT_SHIFT_UP, "<alt-shift-up>"},{KEY_ALT_SHIFT_DOWN, "<alt-shift-down>"},{KEY_ALT_SHIFT_RIGHT, "<alt-shift-right>"},{KEY_ALT_SHIFT_LEFT, "<alt-shift-left>"},
  616. {KEY_SHIFT_TAB,"<shift-tab>"},
  617. {KEY_PASTE_BEGIN,"<paste-begin>"},{KEY_PASTE_END,"<paste-end>"},
  618. };
  619. char * name_from_key(enum Key keycode) {
  620. for (unsigned int i = 0; i < sizeof(KeyNames)/sizeof(KeyNames[0]); ++i) {
  621. if (KeyNames[i].keycode == keycode) return KeyNames[i].name;
  622. }
  623. static char keyNameTmp[8] = {0};
  624. if (keycode <= KEY_CTRL_UNDERSCORE) {
  625. keyNameTmp[0] = '^';
  626. keyNameTmp[1] = '@' + keycode;
  627. keyNameTmp[2] = 0;
  628. return keyNameTmp;
  629. }
  630. to_eight(keycode, keyNameTmp);
  631. return keyNameTmp;
  632. }
  633. struct ColorName color_names[] = {
  634. {"text-fg", &COLOR_FG},
  635. {"text-bg", &COLOR_BG},
  636. {"alternate-fg", &COLOR_ALT_FG},
  637. {"alternate-bg", &COLOR_ALT_BG},
  638. {"number-fg", &COLOR_NUMBER_FG},
  639. {"number-bg", &COLOR_NUMBER_BG},
  640. {"status-fg", &COLOR_STATUS_FG},
  641. {"status-bg", &COLOR_STATUS_BG},
  642. {"status-alt", &COLOR_STATUS_ALT},
  643. {"tabbar-bg", &COLOR_TABBAR_BG},
  644. {"tab-bg", &COLOR_TAB_BG},
  645. {"error-fg", &COLOR_ERROR_FG},
  646. {"error-bg", &COLOR_ERROR_BG},
  647. {"search-fg", &COLOR_SEARCH_FG},
  648. {"search-bg", &COLOR_SEARCH_BG},
  649. {"keyword", &COLOR_KEYWORD},
  650. {"string", &COLOR_STRING},
  651. {"comment", &COLOR_COMMENT},
  652. {"type", &COLOR_TYPE},
  653. {"pragma", &COLOR_PRAGMA},
  654. {"numeral", &COLOR_NUMERAL},
  655. {"select-fg", &COLOR_SELECTFG},
  656. {"select-bg", &COLOR_SELECTBG},
  657. {"red", &COLOR_RED},
  658. {"green", &COLOR_GREEN},
  659. {"bold", &COLOR_BOLD},
  660. {"link", &COLOR_LINK},
  661. {"escape", &COLOR_ESCAPE},
  662. {NULL,NULL},
  663. };
  664. #define FLEXIBLE_ARRAY(name, add_name, type, zero) \
  665. int flex_ ## name ## _count = 0; \
  666. int flex_ ## name ## _space = 0; \
  667. type * name = NULL; \
  668. void add_name (type input) { \
  669. if (flex_ ## name ## _space == 0) { \
  670. flex_ ## name ## _space = 4; \
  671. name = calloc(sizeof(type), flex_ ## name ## _space); \
  672. } else if (flex_ ## name ## _count + 1 == flex_ ## name ## _space) { \
  673. flex_ ## name ## _space *= 2; \
  674. name = realloc(name, sizeof(type) * flex_ ## name ## _space); \
  675. for (int i = flex_ ## name ## _count; i < flex_ ## name ## _space; ++i) name[i] = zero; \
  676. } \
  677. name[flex_ ## name ## _count] = input; \
  678. flex_ ## name ## _count ++; \
  679. }
  680. FLEXIBLE_ARRAY(mappable_actions, add_action, struct action_def, ((struct action_def){NULL,NULL,0,NULL}))
  681. FLEXIBLE_ARRAY(regular_commands, add_command, struct command_def, ((struct command_def){NULL,NULL,NULL}))
  682. FLEXIBLE_ARRAY(prefix_commands, add_prefix_command, struct command_def, ((struct command_def){NULL,NULL,NULL}))
  683. FLEXIBLE_ARRAY(themes, add_colorscheme, struct theme_def, ((struct theme_def){NULL,NULL}))
  684. FLEXIBLE_ARRAY(user_functions, add_user_function, struct bim_function *, NULL)
  685. /**
  686. * Special implementation of getch with a timeout
  687. */
  688. int _bim_unget = -1;
  689. void bim_unget(int c) {
  690. _bim_unget = c;
  691. }
  692. int bim_getch_timeout(int timeout) {
  693. fflush(stdout);
  694. if (_bim_unget != -1) {
  695. int out = _bim_unget;
  696. _bim_unget = -1;
  697. return out;
  698. }
  699. struct pollfd fds[1];
  700. fds[0].fd = global_config.tty_in;
  701. fds[0].events = POLLIN;
  702. int ret = poll(fds,1,timeout);
  703. if (ret > 0 && fds[0].revents & POLLIN) {
  704. unsigned char buf[1];
  705. read(global_config.tty_in, buf, 1);
  706. return buf[0];
  707. } else {
  708. return -1;
  709. }
  710. }
  711. /**
  712. * UTF-8 parser state
  713. */
  714. static uint32_t codepoint_r;
  715. static uint32_t state = 0;
  716. #define UTF8_ACCEPT 0
  717. #define UTF8_REJECT 1
  718. static inline uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
  719. static int state_table[32] = {
  720. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xxxxxxx */
  721. 1,1,1,1,1,1,1,1, /* 10xxxxxx */
  722. 2,2,2,2, /* 110xxxxx */
  723. 3,3, /* 1110xxxx */
  724. 4, /* 11110xxx */
  725. 1 /* 11111xxx */
  726. };
  727. static int mask_bytes[32] = {
  728. 0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  729. 0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  730. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  731. 0x1F,0x1F,0x1F,0x1F,
  732. 0x0F,0x0F,
  733. 0x07,
  734. 0x00
  735. };
  736. static int next[5] = {
  737. 0,
  738. 1,
  739. 0,
  740. 2,
  741. 3
  742. };
  743. if (*state == UTF8_ACCEPT) {
  744. *codep = byte & mask_bytes[byte >> 3];
  745. *state = state_table[byte >> 3];
  746. } else if (*state > 0) {
  747. *codep = (byte & 0x3F) | (*codep << 6);
  748. *state = next[*state];
  749. }
  750. return *state;
  751. }
  752. #define shift_key(i) _shift_key((i), this_buf, &timeout);
  753. int _shift_key(int i, int this_buf[20], int *timeout) {
  754. int thing = this_buf[*timeout-1];
  755. (*timeout) = 0;
  756. switch (thing) {
  757. /* There are other combinations we can handle... */
  758. case '2': return i + 4;
  759. case '5': return i + 8;
  760. case '3': return i + 12;
  761. case '4': return i + 16;
  762. default: return i;
  763. }
  764. }
  765. int bim_getkey(int read_timeout) {
  766. int timeout = 0;
  767. int this_buf[20];
  768. int cin;
  769. uint32_t c;
  770. uint32_t istate = 0;
  771. while ((cin = bim_getch_timeout(read_timeout))) {
  772. if (cin == -1) {
  773. if (timeout && this_buf[timeout-1] == '\033')
  774. return KEY_ESCAPE;
  775. return KEY_TIMEOUT;
  776. }
  777. if (!decode(&istate, &c, cin)) {
  778. if (timeout == 0) {
  779. switch (c) {
  780. case '\033':
  781. if (timeout == 0) {
  782. this_buf[timeout] = c;
  783. timeout++;
  784. }
  785. continue;
  786. case KEY_LINEFEED: return KEY_ENTER;
  787. case KEY_DELETE: return KEY_BACKSPACE;
  788. }
  789. return c;
  790. } else {
  791. if (timeout >= 1 && this_buf[timeout-1] == '\033' && c == '\033') {
  792. bim_unget(c);
  793. timeout = 0;
  794. return KEY_ESCAPE;
  795. }
  796. if (timeout >= 1 && this_buf[0] == '\033' && c == 'O') {
  797. this_buf[timeout] = c;
  798. timeout++;
  799. continue;
  800. }
  801. if (timeout >= 2 && this_buf[0] == '\033' && this_buf[1] == 'O') {
  802. switch (c) {
  803. case 'P': timeout = 0; return KEY_F1;
  804. case 'Q': timeout = 0; return KEY_F2;
  805. case 'R': timeout = 0; return KEY_F3;
  806. case 'S': timeout = 0; return KEY_F4;
  807. }
  808. timeout = 0;
  809. continue;
  810. }
  811. if (timeout >= 1 && this_buf[timeout-1] == '\033' && c != '[') {
  812. timeout = 0;
  813. bim_unget(c);
  814. return KEY_ESCAPE;
  815. }
  816. if (timeout >= 1 && this_buf[timeout-1] == '\033' && c == '[') {
  817. timeout = 1;
  818. this_buf[timeout] = c;
  819. timeout++;
  820. continue;
  821. }
  822. if (timeout >= 2 && this_buf[0] == '\033' && this_buf[1] == '[' &&
  823. (isdigit(c) || (c == ';'))) {
  824. this_buf[timeout] = c;
  825. timeout++;
  826. continue;
  827. }
  828. if (timeout >= 2 && this_buf[0] == '\033' && this_buf[1] == '[') {
  829. switch (c) {
  830. case 'M': timeout = 0; return KEY_MOUSE;
  831. case '<': timeout = 0; return KEY_MOUSE_SGR;
  832. case 'A': return shift_key(KEY_UP);
  833. case 'B': return shift_key(KEY_DOWN);
  834. case 'C': return shift_key(KEY_RIGHT);
  835. case 'D': return shift_key(KEY_LEFT);
  836. case 'H': timeout = 0; return KEY_HOME;
  837. case 'F': timeout = 0; return KEY_END;
  838. case 'I': timeout = 0; return KEY_PAGE_UP;
  839. case 'G': timeout = 0; return KEY_PAGE_DOWN;
  840. case 'Z': timeout = 0; return KEY_SHIFT_TAB;
  841. case '~':
  842. if (timeout == 3) {
  843. switch (this_buf[2]) {
  844. case '1': timeout = 0; return KEY_HOME;
  845. case '3': timeout = 0; return KEY_DELETE;
  846. case '4': timeout = 0; return KEY_END;
  847. case '5': timeout = 0; return KEY_PAGE_UP;
  848. case '6': timeout = 0; return KEY_PAGE_DOWN;
  849. }
  850. } else if (timeout == 5) {
  851. if (this_buf[2] == '2' && this_buf[3] == '0' && this_buf[4] == '0') {
  852. return KEY_PASTE_BEGIN;
  853. } else if (this_buf[2] == '2' && this_buf[3] == '0' && this_buf[4] == '1') {
  854. return KEY_PASTE_END;
  855. }
  856. } else if (this_buf[2] == '1') {
  857. switch (this_buf[3]) {
  858. case '5': return KEY_F5;
  859. case '7': return KEY_F6;
  860. case '8': return KEY_F7;
  861. case '9': return KEY_F8;
  862. }
  863. } else if (this_buf[2] == '2') {
  864. switch (this_buf[3]) {
  865. case '0': return KEY_F9;
  866. case '1': return KEY_F10;
  867. case '3': return KEY_F11;
  868. case '4': return KEY_F12;
  869. }
  870. }
  871. break;
  872. }
  873. }
  874. timeout = 0;
  875. continue;
  876. }
  877. } else if (istate == UTF8_REJECT) {
  878. istate = 0;
  879. }
  880. }
  881. return KEY_TIMEOUT;
  882. }
  883. enum Key key_from_name(char * name) {
  884. for (unsigned int i = 0; i < sizeof(KeyNames)/sizeof(KeyNames[0]); ++i) {
  885. if (!strcmp(KeyNames[i].name, name)) return KeyNames[i].keycode;
  886. }
  887. if (name[0] == '^' && name[1] && !name[2]) {
  888. return name[1] - '@';
  889. }
  890. if (!name[1]) return name[0];
  891. /* Try decoding */
  892. uint32_t c, state = 0;
  893. int candidate = -1;
  894. while (*name) {
  895. if (!decode(&state, &c, (unsigned char)*name)) {
  896. if (candidate == -1) candidate = c;
  897. else return -1; /* Multiple characters */
  898. } else if (state == UTF8_REJECT) {
  899. return -1;
  900. }
  901. }
  902. return candidate;
  903. }
  904. /**
  905. * Pointer to current active buffer
  906. */
  907. buffer_t * env = NULL;
  908. buffer_t * left_buffer = NULL;
  909. buffer_t * right_buffer = NULL;
  910. /**
  911. * A buffer for holding a number (line, repetition count)
  912. */
  913. char nav_buf[NAV_BUFFER_MAX+1];
  914. int nav_buffer = 0;
  915. /**
  916. * Available buffers
  917. */
  918. int buffers_len = 0;
  919. int buffers_avail = 0;
  920. buffer_t ** buffers = NULL;
  921. /**
  922. * Create a new buffer
  923. */
  924. buffer_t * buffer_new(void) {
  925. if (buffers_len == buffers_avail) {
  926. /* If we are out of buffer space, expand the buffers vector */
  927. buffers_avail *= 2;
  928. buffers = realloc(buffers, sizeof(buffer_t *) * buffers_avail);
  929. }
  930. /* TODO: Support having split buffers with more than two buffers open */
  931. if (left_buffer) {
  932. left_buffer->left = 0;
  933. left_buffer->width = global_config.term_width;
  934. right_buffer->left = 0;
  935. right_buffer->width = global_config.term_width;
  936. left_buffer = NULL;
  937. right_buffer = NULL;
  938. }
  939. /* Allocate a new buffer */
  940. buffers[buffers_len] = malloc(sizeof(buffer_t));
  941. memset(buffers[buffers_len], 0x00, sizeof(buffer_t));
  942. buffers[buffers_len]->left = 0;
  943. buffers[buffers_len]->width = global_config.term_width;
  944. buffers[buffers_len]->highlighting_paren = -1;
  945. buffers[buffers_len]->numbers = global_config.numbers;
  946. buffers[buffers_len]->gutter = 1;
  947. buffers_len++;
  948. global_config.tabs_visible = (!global_config.autohide_tabs) || (buffers_len > 1);
  949. return buffers[buffers_len-1];
  950. }
  951. /**
  952. * Open the biminfo file.
  953. */
  954. FILE * open_biminfo(void) {
  955. /* TODO This should probably be configurable line bimrc */
  956. char * home = getenv("HOME");
  957. if (!home) {
  958. /* Since it's not, we need HOME */
  959. return NULL;
  960. }
  961. /* biminfo lives at ~/.biminfo */
  962. char biminfo_path[PATH_MAX+1] = {0};
  963. sprintf(biminfo_path,"%s/.biminfo",home);
  964. /* Try to open normally first... */
  965. FILE * biminfo = fopen(biminfo_path,"r+");
  966. if (!biminfo) {
  967. /* Otherwise, try to create it. */
  968. biminfo = fopen(biminfo_path,"w+");
  969. }
  970. return biminfo;
  971. }
  972. /**
  973. * Fetch the cursor position from a biminfo file
  974. */
  975. int fetch_from_biminfo(buffer_t * buf) {
  976. /* Can't fetch if we don't have a filename */
  977. if (!buf->file_name) return 1;
  978. /* Get the absolute name of the file */
  979. char tmp_path[PATH_MAX+2];
  980. if (!realpath(buf->file_name, tmp_path)) {
  981. return 1;
  982. }
  983. strcat(tmp_path," ");
  984. FILE * biminfo = open_biminfo();
  985. if (!biminfo) return 1;
  986. /* Scan */
  987. char line[PATH_MAX+64];
  988. while (!feof(biminfo)) {
  989. fpos_t start_of_line;
  990. fgetpos(biminfo, &start_of_line);
  991. fgets(line, PATH_MAX+63, biminfo);
  992. if (line[0] != '>') {
  993. continue;
  994. }
  995. if (!strncmp(&line[1],tmp_path, strlen(tmp_path))) {
  996. /* Read */
  997. sscanf(line+1+strlen(tmp_path)+1,"%d",&buf->line_no);
  998. sscanf(line+1+strlen(tmp_path)+21,"%d",&buf->col_no);
  999. if (buf->line_no > buf->line_count) buf->line_no = buf->line_count;
  1000. if (buf->col_no > buf->lines[buf->line_no-1]->actual) buf->col_no = buf->lines[buf->line_no-1]->actual;
  1001. try_to_center();
  1002. return 0;
  1003. }
  1004. }
  1005. return 0;
  1006. }
  1007. /**
  1008. * Write a file containing the last cursor position of a buffer.
  1009. */
  1010. int update_biminfo(buffer_t * buf) {
  1011. if (!buf->file_name) return 1;
  1012. /* Get the absolute name of the file */
  1013. char tmp_path[PATH_MAX+1];
  1014. if (!realpath(buf->file_name, tmp_path)) {
  1015. return 1;
  1016. }
  1017. strcat(tmp_path," ");
  1018. FILE * biminfo = open_biminfo();
  1019. if (!biminfo) return 1;
  1020. /* Scan */
  1021. char line[PATH_MAX+64];
  1022. while (!feof(biminfo)) {
  1023. fpos_t start_of_line;
  1024. fgetpos(biminfo, &start_of_line);
  1025. fgets(line, PATH_MAX+63, biminfo);
  1026. if (line[0] != '>') {
  1027. continue;
  1028. }
  1029. if (!strncmp(&line[1],tmp_path, strlen(tmp_path))) {
  1030. /* Update */
  1031. fsetpos(biminfo, &start_of_line);
  1032. fprintf(biminfo,">%s %20d %20d\n", tmp_path, buf->line_no, buf->col_no);
  1033. goto _done;
  1034. }
  1035. }
  1036. if (ftell(biminfo) == 0) {
  1037. /* New biminfo */
  1038. fprintf(biminfo, "# This is a biminfo file.\n");
  1039. fprintf(biminfo, "# It was generated by bim. Do not edit it by hand!\n");
  1040. fprintf(biminfo, "# Cursor positions and other state are stored here.\n");
  1041. }
  1042. /* Haven't found what we're looking for, should be at end of file */
  1043. fprintf(biminfo, ">%s %20d %20d\n", tmp_path, buf->line_no, buf->col_no);
  1044. _done:
  1045. fclose(biminfo);
  1046. return 0;
  1047. }
  1048. /**
  1049. * Close a buffer
  1050. */
  1051. buffer_t * buffer_close(buffer_t * buf) {
  1052. int i;
  1053. /* Locate the buffer in the buffer pointer vector */
  1054. for (i = 0; i < buffers_len; i++) {
  1055. if (buf == buffers[i])
  1056. break;
  1057. }
  1058. /* Invalid buffer? */
  1059. if (i == buffers_len) {
  1060. return env; /* wtf */
  1061. }
  1062. update_biminfo(buf);
  1063. /* Clean up lines used by old buffer */
  1064. for (int i = 0; i < buf->line_count; ++i) {
  1065. free(buf->lines[i]);
  1066. }
  1067. free(buf->lines);
  1068. if (buf->file_name) {
  1069. free(buf->file_name);
  1070. }
  1071. history_t * h = buf->history;
  1072. while (h->next) {
  1073. h = h->next;
  1074. }
  1075. while (h) {
  1076. history_t * x = h->previous;
  1077. free(h);
  1078. h = x;
  1079. }
  1080. /* Clean up the old buffer */
  1081. free(buf);
  1082. /* Remove the buffer from the vector, moving others up */
  1083. if (i != buffers_len - 1) {
  1084. memmove(&buffers[i], &buffers[i+1], sizeof(*buffers) * (buffers_len - i));
  1085. }
  1086. /* There is one less buffer */
  1087. buffers_len--;
  1088. if (buffers_len && global_config.tab_offset >= buffers_len) global_config.tab_offset--;
  1089. global_config.tabs_visible = (!global_config.autohide_tabs) || (buffers_len > 1);
  1090. if (!buffers_len) {
  1091. /* There are no more buffers. */
  1092. return NULL;
  1093. }
  1094. /* If this was the last buffer, return the previous last buffer */
  1095. if (i == buffers_len) {
  1096. return buffers[buffers_len-1];
  1097. }
  1098. /* Otherwise return the new last buffer */
  1099. return buffers[i];
  1100. }
  1101. /**
  1102. * Theming data
  1103. *
  1104. * This default set is pretty simple "default foreground on default background"
  1105. * except for search and selections which are black-on-white specifically.
  1106. *
  1107. * The theme colors get set by separate configurable themes.
  1108. */
  1109. const char * COLOR_FG = "@9";
  1110. const char * COLOR_BG = "@9";
  1111. const char * COLOR_ALT_FG = "@9";
  1112. const char * COLOR_ALT_BG = "@9";
  1113. const char * COLOR_NUMBER_FG = "@9";
  1114. const char * COLOR_NUMBER_BG = "@9";
  1115. const char * COLOR_STATUS_FG = "@9";
  1116. const char * COLOR_STATUS_BG = "@9";
  1117. const char * COLOR_STATUS_ALT= "@9";
  1118. const char * COLOR_TABBAR_BG = "@9";
  1119. const char * COLOR_TAB_BG = "@9";
  1120. const char * COLOR_ERROR_FG = "@9";
  1121. const char * COLOR_ERROR_BG = "@9";
  1122. const char * COLOR_SEARCH_FG = "@0";
  1123. const char * COLOR_SEARCH_BG = "@17";
  1124. const char * COLOR_KEYWORD = "@9";
  1125. const char * COLOR_STRING = "@9";
  1126. const char * COLOR_COMMENT = "@9";
  1127. const char * COLOR_TYPE = "@9";
  1128. const char * COLOR_PRAGMA = "@9";
  1129. const char * COLOR_NUMERAL = "@9";
  1130. const char * COLOR_SELECTFG = "@0";
  1131. const char * COLOR_SELECTBG = "@17";
  1132. const char * COLOR_RED = "@1";
  1133. const char * COLOR_GREEN = "@2";
  1134. const char * COLOR_BOLD = "@9";
  1135. const char * COLOR_LINK = "@9";
  1136. const char * COLOR_ESCAPE = "@9";
  1137. const char * current_theme = "none";
  1138. /**
  1139. * Convert syntax highlighting flag to color code
  1140. */
  1141. const char * flag_to_color(int _flag) {
  1142. int flag = _flag & 0xF;
  1143. switch (flag) {
  1144. case FLAG_KEYWORD:
  1145. return COLOR_KEYWORD;
  1146. case FLAG_STRING:
  1147. return COLOR_STRING;
  1148. case FLAG_COMMENT:
  1149. return COLOR_COMMENT;
  1150. case FLAG_TYPE:
  1151. return COLOR_TYPE;
  1152. case FLAG_NUMERAL:
  1153. return COLOR_NUMERAL;
  1154. case FLAG_PRAGMA:
  1155. return COLOR_PRAGMA;
  1156. case FLAG_DIFFPLUS:
  1157. return COLOR_GREEN;
  1158. case FLAG_DIFFMINUS:
  1159. return COLOR_RED;
  1160. case FLAG_SELECT:
  1161. return COLOR_FG;
  1162. case FLAG_BOLD:
  1163. return COLOR_BOLD;
  1164. case FLAG_LINK:
  1165. return COLOR_LINK;
  1166. case FLAG_ESCAPE:
  1167. return COLOR_ESCAPE;
  1168. default:
  1169. return COLOR_FG;
  1170. }
  1171. }
  1172. /**
  1173. * Find keywords from a list and paint them, assuming they aren't in the middle of other words.
  1174. * Returns 1 if a keyword from the last was found, otherwise 0.
  1175. */
  1176. int find_keywords(struct syntax_state * state, char ** keywords, int flag, int (*keyword_qualifier)(int c)) {
  1177. if (keyword_qualifier(lastchar())) return 0;
  1178. if (!keyword_qualifier(charat())) return 0;
  1179. for (char ** keyword = keywords; *keyword; ++keyword) {
  1180. int d = 0;
  1181. while (state->i + d < state->line->actual && state->line->text[state->i+d].codepoint == (*keyword)[d]) d++;
  1182. if ((*keyword)[d] == '\0' && (state->i + d >= state->line->actual || !keyword_qualifier(state->line->text[state->i+d].codepoint))) {
  1183. paint((int)strlen(*keyword), flag);
  1184. return 1;
  1185. }
  1186. }
  1187. return 0;
  1188. }
  1189. /**
  1190. * Match and paint a single keyword. Returns 1 if the keyword was matched and 0 otherwise,
  1191. * so it can be used for prefix checking for things that need further special handling.
  1192. */
  1193. int match_and_paint(struct syntax_state * state, const char * keyword, int flag, int (*keyword_qualifier)(int c)) {
  1194. if (keyword_qualifier(lastchar())) return 0;
  1195. if (!keyword_qualifier(charat())) return 0;
  1196. int i = state->i;
  1197. int slen = 0;
  1198. while (i < state->line->actual || *keyword == '\0') {
  1199. if (*keyword == '\0' && (i >= state->line->actual || !keyword_qualifier(state->line->text[i].codepoint))) {
  1200. for (int j = 0; j < slen; ++j) {
  1201. paint(1, flag);
  1202. }
  1203. return 1;
  1204. }
  1205. if (*keyword != state->line->text[i].codepoint) return 0;
  1206. i++;
  1207. keyword++;
  1208. slen++;
  1209. }
  1210. return 0;
  1211. }
  1212. void paint_single_string(struct syntax_state * state) {
  1213. paint(1, FLAG_STRING);
  1214. while (charat() != -1) {
  1215. if (charat() == '\\' && nextchar() == '\'') {
  1216. paint(2, FLAG_ESCAPE);
  1217. } else if (charat() == '\'') {
  1218. paint(1, FLAG_STRING);
  1219. return;
  1220. } else if (charat() == '\\') {
  1221. paint(2, FLAG_ESCAPE);
  1222. } else {
  1223. paint(1, FLAG_STRING);
  1224. }
  1225. }
  1226. }
  1227. void paint_simple_string(struct syntax_state * state) {
  1228. /* Assumes you came in from a check of charat() == '"' */
  1229. paint(1, FLAG_STRING);
  1230. while (charat() != -1) {
  1231. if (charat() == '\\' && nextchar() == '"') {
  1232. paint(2, FLAG_ESCAPE);
  1233. } else if (charat() == '"') {
  1234. paint(1, FLAG_STRING);
  1235. return;
  1236. } else if (charat() == '\\') {
  1237. paint(2, FLAG_ESCAPE);
  1238. } else {
  1239. paint(1, FLAG_STRING);
  1240. }
  1241. }
  1242. }
  1243. /**
  1244. * This is a basic character matcher for "keyword" characters.
  1245. */
  1246. int simple_keyword_qualifier(int c) {
  1247. return isalnum(c) || (c == '_');
  1248. }
  1249. /**
  1250. * These words can appear in comments and should be highlighted.
  1251. * Since there are a lot of comment highlighters, let's break them out.
  1252. */
  1253. int common_comment_buzzwords(struct syntax_state * state) {
  1254. if (match_and_paint(state, "TODO", FLAG_NOTICE, simple_keyword_qualifier)) { return 1; }
  1255. else if (match_and_paint(state, "XXX", FLAG_NOTICE, simple_keyword_qualifier)) { return 1; }
  1256. else if (match_and_paint(state, "FIXME", FLAG_ERROR, simple_keyword_qualifier)) { return 1; }
  1257. return 0;
  1258. }
  1259. /**
  1260. * Paint a comment until end of line, assumes this comment can not continue.
  1261. * (Some languages have comments that can continue with a \ - don't use this!)
  1262. * Assumes you've already painted your comment start characters.
  1263. */
  1264. int paint_comment(struct syntax_state * state) {
  1265. while (charat() != -1) {
  1266. if (common_comment_buzzwords(state)) continue;
  1267. else { paint(1, FLAG_COMMENT); }
  1268. }
  1269. return -1;
  1270. }
  1271. /**
  1272. * Match a word forward and return whether it was matched.
  1273. */
  1274. int match_forward(struct syntax_state * state, char * c) {
  1275. int i = 0;
  1276. while (1) {
  1277. if (charrel(i) == -1 && !*c) return 1;
  1278. if (charrel(i) != *c) return 0;
  1279. c++;
  1280. i++;
  1281. }
  1282. return 0;
  1283. }
  1284. /**
  1285. * Find and return a highlighter by name, or NULL
  1286. */
  1287. struct syntax_definition * find_syntax_calculator(const char * name) {
  1288. for (struct syntax_definition * s = syntaxes; syntaxes && s->name; ++s) {
  1289. if (!strcmp(s->name, name)) {
  1290. return s;
  1291. }
  1292. }
  1293. return NULL;
  1294. }
  1295. int syntax_count = 0;
  1296. int syntax_space = 0;
  1297. struct syntax_definition * syntaxes = NULL;
  1298. void add_syntax(struct syntax_definition def) {
  1299. if (syntax_space == 0) {
  1300. syntax_space = 4;
  1301. syntaxes = calloc(sizeof(struct syntax_definition), syntax_space);
  1302. } else if (syntax_count +1 == syntax_space) {
  1303. syntax_space *= 2;
  1304. syntaxes = realloc(syntaxes, sizeof(struct syntax_definition) * syntax_space);
  1305. for (int i = syntax_count; i < syntax_space; ++i) syntaxes[i].name = NULL;
  1306. }
  1307. syntaxes[syntax_count] = def;
  1308. syntax_count++;
  1309. }
  1310. /**
  1311. * Calculate syntax highlighting for the given line.
  1312. */
  1313. void recalculate_syntax(line_t * line, int line_no) {
  1314. /* Clear syntax for this line first */
  1315. int is_original = 1;
  1316. while (1) {
  1317. for (int i = 0; i < line->actual; ++i) {
  1318. line->text[i].flags = 0;
  1319. }
  1320. if (!env->syntax) {
  1321. if (line_no != -1) rehighlight_search(line);
  1322. return;
  1323. }
  1324. /* Start from the line's stored in initial state */
  1325. struct syntax_state state;
  1326. state.line = line;
  1327. state.line_no = line_no;
  1328. state.state = line->istate;
  1329. state.i = 0;
  1330. while (1) {
  1331. state.state = env->syntax->calculate(&state);
  1332. if (state.state != 0) {
  1333. if (line_no == -1) return;
  1334. rehighlight_search(line);
  1335. if (!is_original) {
  1336. redraw_line(line_no);
  1337. }
  1338. if (line_no + 1 < env->line_count && env->lines[line_no+1]->istate != state.state) {
  1339. line_no++;
  1340. line = env->lines[line_no];
  1341. line->istate = state.state;
  1342. if (env->loading) return;
  1343. is_original = 0;
  1344. goto _next;
  1345. }
  1346. return;
  1347. }
  1348. }
  1349. _next:
  1350. (void)0;
  1351. }
  1352. }
  1353. /**
  1354. * Recalculate tab widths.
  1355. */
  1356. void recalculate_tabs(line_t * line) {
  1357. if (env->loading) return;
  1358. int j = 0;
  1359. for (int i = 0; i < line->actual; ++i) {
  1360. if (line->text[i].codepoint == '\t') {
  1361. line->text[i].display_width = env->tabstop - (j % env->tabstop);
  1362. }
  1363. j += line->text[i].display_width;
  1364. }
  1365. }
  1366. /**
  1367. * TODO:
  1368. *
  1369. * The line editing functions should probably take a buffer_t *
  1370. * so that they can act on buffers other than the active one.
  1371. */
  1372. void recursive_history_free(history_t * root) {
  1373. if (!root->next) return;
  1374. history_t * n = root->next;
  1375. recursive_history_free(n);
  1376. switch (n->type) {
  1377. case HISTORY_REPLACE_LINE:
  1378. free(n->contents.remove_replace_line.contents);
  1379. /* fall-through */
  1380. case HISTORY_REMOVE_LINE:
  1381. free(n->contents.remove_replace_line.old_contents);
  1382. break;
  1383. default:
  1384. /* Nothing extra to free */
  1385. break;
  1386. }
  1387. free(n);
  1388. root->next = NULL;
  1389. }
  1390. #define HIST_APPEND(e) do { \
  1391. e->col = env->col_no; \
  1392. e->line = env->line_no; \
  1393. if (env->history) { \
  1394. e->previous = env->history; \
  1395. recursive_history_free(env->history); \
  1396. env->history->next = e; \
  1397. e->next = NULL; \
  1398. } \
  1399. env->history = e; \
  1400. } while (0)
  1401. /**
  1402. * Mark a point where a complete set of actions has ended.
  1403. */
  1404. void set_history_break(void) {
  1405. if (!global_config.history_enabled) return;
  1406. if (env->history->type != HISTORY_BREAK && env->history->type != HISTORY_SENTINEL) {
  1407. history_t * e = malloc(sizeof(history_t));
  1408. e->type = HISTORY_BREAK;
  1409. HIST_APPEND(e);
  1410. }
  1411. }
  1412. /**
  1413. * Insert a character into an existing line.
  1414. */
  1415. __attribute__((warn_unused_result)) line_t * line_insert(line_t * line, char_t c, int offset, int lineno) {
  1416. if (!env->loading && global_config.history_enabled && lineno != -1) {
  1417. history_t * e = malloc(sizeof(history_t));
  1418. e->type = HISTORY_INSERT;
  1419. e->contents.insert_delete_replace.lineno = lineno;
  1420. e->contents.insert_delete_replace.offset = offset;
  1421. e->contents.insert_delete_replace.codepoint = c.codepoint;
  1422. HIST_APPEND(e);
  1423. }
  1424. /* If there is not enough space... */
  1425. if (line->actual == line->available) {
  1426. /* Expand the line buffer */
  1427. if (line->available == 0) {
  1428. line->available = 8;
  1429. } else {
  1430. line->available *= 2;
  1431. }
  1432. line = realloc(line, sizeof(line_t) + sizeof(char_t) * line->available);
  1433. }
  1434. /* If this was not the last character, then shift remaining characters forward. */
  1435. if (offset < line->actual) {
  1436. memmove(&line->text[offset+1], &line->text[offset], sizeof(char_t) * (line->actual - offset));
  1437. }
  1438. /* Insert the new character */
  1439. line->text[offset] = c;
  1440. /* There is one new character in the line */
  1441. line->actual += 1;
  1442. if (!env->loading) {
  1443. line->rev_status = 2; /* Modified */
  1444. recalculate_tabs(line);
  1445. recalculate_syntax(line, lineno);
  1446. }
  1447. return line;
  1448. }
  1449. /**
  1450. * Delete a character from a line
  1451. */
  1452. void line_delete(line_t * line, int offset, int lineno) {
  1453. /* Can't delete character before start of line. */
  1454. if (offset == 0) return;
  1455. if (!env->loading && global_config.history_enabled && lineno != -1) {
  1456. history_t * e = malloc(sizeof(history_t));
  1457. e->type = HISTORY_DELETE;
  1458. e->contents.insert_delete_replace.lineno = lineno;
  1459. e->contents.insert_delete_replace.offset = offset;
  1460. e->contents.insert_delete_replace.old_codepoint = line->text[offset-1].codepoint;
  1461. HIST_APPEND(e);
  1462. }
  1463. /* If this isn't the last character, we need to move all subsequent characters backwards */
  1464. if (offset < line->actual) {
  1465. memmove(&line->text[offset-1], &line->text[offset], sizeof(char_t) * (line->actual - offset));
  1466. }
  1467. /* The line is one character shorter */
  1468. line->actual -= 1;
  1469. line->rev_status = 2;
  1470. recalculate_tabs(line);
  1471. recalculate_syntax(line, lineno);
  1472. }
  1473. /**
  1474. * Replace a character in a line
  1475. */
  1476. void line_replace(line_t * line, char_t _c, int offset, int lineno) {
  1477. if (!env->loading && global_config.history_enabled && lineno != -1) {
  1478. history_t * e = malloc(sizeof(history_t));
  1479. e->type = HISTORY_REPLACE;
  1480. e->contents.insert_delete_replace.lineno = lineno;
  1481. e->contents.insert_delete_replace.offset = offset;
  1482. e->contents.insert_delete_replace.codepoint = _c.codepoint;
  1483. e->contents.insert_delete_replace.old_codepoint = line->text[offset].codepoint;
  1484. HIST_APPEND(e);
  1485. }
  1486. line->text[offset] = _c;
  1487. if (!env->loading) {
  1488. line->rev_status = 2; /* Modified */
  1489. recalculate_tabs(line);
  1490. recalculate_syntax(line, lineno);
  1491. }
  1492. }
  1493. /**
  1494. * Remove a line from the active buffer
  1495. */
  1496. line_t ** remove_line(line_t ** lines, int offset) {
  1497. /* If there is only one line, clear it instead of removing it. */
  1498. if (env->line_count == 1) {
  1499. while (lines[offset]->actual > 0) {
  1500. line_delete(lines[offset], lines[offset]->actual, offset);
  1501. }
  1502. return lines;
  1503. }
  1504. if (!env->loading && global_config.history_enabled) {
  1505. history_t * e = malloc(sizeof(history_t));
  1506. e->type = HISTORY_REMOVE_LINE;
  1507. e->contents.remove_replace_line.lineno = offset;
  1508. e->contents.remove_replace_line.old_contents = malloc(sizeof(line_t) + sizeof(char_t) * lines[offset]->available);
  1509. memcpy(e->contents.remove_replace_line.old_contents, lines[offset], sizeof(line_t) + sizeof(char_t) * lines[offset]->available);
  1510. HIST_APPEND(e);
  1511. }
  1512. /* Otherwise, free the data used by the line */
  1513. free(lines[offset]);
  1514. /* Move other lines up */
  1515. if (offset < env->line_count-1) {
  1516. memmove(&lines[offset], &lines[offset+1], sizeof(line_t *) * (env->line_count - (offset - 1)));
  1517. lines[env->line_count-1] = NULL;
  1518. }
  1519. /* There is one less line */
  1520. env->line_count -= 1;
  1521. return lines;
  1522. }
  1523. /**
  1524. * Add a new line to the active buffer.
  1525. */
  1526. line_t ** add_line(line_t ** lines, int offset) {
  1527. /* Invalid offset? */
  1528. if (offset > env->line_count) return lines;
  1529. if (!env->loading && global_config.history_enabled) {
  1530. history_t * e = malloc(sizeof(history_t));
  1531. e->type = HISTORY_ADD_LINE;
  1532. e->contents.add_merge_split_lines.lineno = offset;
  1533. HIST_APPEND(e);
  1534. }
  1535. /* Not enough space */
  1536. if (env->line_count == env->line_avail) {
  1537. /* Allocate more space */
  1538. env->line_avail *= 2;
  1539. lines = realloc(lines, sizeof(line_t *) * env->line_avail);
  1540. }
  1541. /* If this isn't the last line, move other lines down */
  1542. if (offset < env->line_count) {
  1543. memmove(&lines[offset+1], &lines[offset], sizeof(line_t *) * (env->line_count - offset));
  1544. }
  1545. /* Allocate the new line */
  1546. lines[offset] = calloc(sizeof(line_t) + sizeof(char_t) * 32, 1);
  1547. lines[offset]->available = 32;
  1548. /* There is one new line */
  1549. env->line_count += 1;
  1550. env->lines = lines;
  1551. if (!env->loading) {
  1552. lines[offset]->rev_status = 2; /* Modified */
  1553. }
  1554. if (offset > 0 && !env->loading) {
  1555. recalculate_syntax(lines[offset-1],offset-1);
  1556. }
  1557. return lines;
  1558. }
  1559. /**
  1560. * Replace a line with data from another line (used by paste to paste yanked lines)
  1561. */
  1562. void replace_line(line_t ** lines, int offset, line_t * replacement) {
  1563. if (!env->loading && global_config.history_enabled) {
  1564. history_t * e = malloc(sizeof(history_t));
  1565. e->type = HISTORY_REPLACE_LINE;
  1566. e->contents.remove_replace_line.lineno = offset;
  1567. e->contents.remove_replace_line.old_contents = malloc(sizeof(line_t) + sizeof(char_t) * lines[offset]->available);
  1568. memcpy(e->contents.remove_replace_line.old_contents, lines[offset], sizeof(line_t) + sizeof(char_t) * lines[offset]->available);
  1569. e->contents.remove_replace_line.contents = malloc(sizeof(line_t) + sizeof(char_t) * replacement->available);
  1570. memcpy(e->contents.remove_replace_line.contents, replacement, sizeof(line_t) + sizeof(char_t) * replacement->available);
  1571. HIST_APPEND(e);
  1572. }
  1573. if (lines[offset]->available < replacement->actual) {
  1574. lines[offset] = realloc(lines[offset], sizeof(line_t) + sizeof(char_t) * replacement->available);
  1575. lines[offset]->available = replacement->available;
  1576. }
  1577. lines[offset]->actual = replacement->actual;
  1578. memcpy(&lines[offset]->text, &replacement->text, sizeof(char_t) * replacement->actual);
  1579. if (!env->loading) {
  1580. lines[offset]->rev_status = 2;
  1581. recalculate_syntax(lines[offset],offset);
  1582. }
  1583. }
  1584. /**
  1585. * Merge two consecutive lines.
  1586. * lineb is the offset of the second line.
  1587. */
  1588. line_t ** merge_lines(line_t ** lines, int lineb) {
  1589. /* linea is the line immediately before lineb */
  1590. int linea = lineb - 1;
  1591. if (!env->loading && global_config.history_enabled) {
  1592. history_t * e = malloc(sizeof(history_t));
  1593. e->type = HISTORY_MERGE_LINES;
  1594. e->contents.add_merge_split_lines.lineno = lineb;
  1595. e->contents.add_merge_split_lines.split = env->lines[linea]->actual;
  1596. HIST_APPEND(e);
  1597. }
  1598. /* If there isn't enough space in linea hold both... */
  1599. while (lines[linea]->available < lines[linea]->actual + lines[lineb]->actual) {
  1600. /* ... allocate more space until it fits */
  1601. if (lines[linea]->available == 0) {
  1602. lines[linea]->available = 8;
  1603. } else {
  1604. lines[linea]->available *= 2;
  1605. }
  1606. /* XXX why not just do this once after calculating appropriate size */
  1607. lines[linea] = realloc(lines[linea], sizeof(line_t) + sizeof(char_t) * lines[linea]->available);
  1608. }
  1609. /* Copy the second line into the first line */
  1610. memcpy(&lines[linea]->text[lines[linea]->actual], &lines[lineb]->text, sizeof(char_t) * lines[lineb]->actual);
  1611. /* The first line is now longer */
  1612. lines[linea]->actual = lines[linea]->actual + lines[lineb]->actual;
  1613. if (!env->loading) {
  1614. lines[linea]->rev_status = 2;
  1615. recalculate_tabs(lines[linea]);
  1616. recalculate_syntax(lines[linea], linea);
  1617. }
  1618. /* Remove the second line */
  1619. free(lines[lineb]);
  1620. /* Move other lines up */
  1621. if (lineb < env->line_count) {
  1622. memmove(&lines[lineb], &lines[lineb+1], sizeof(line_t *) * (env->line_count - (lineb - 1)));
  1623. lines[env->line_count-1] = NULL;
  1624. }
  1625. /* There is one less line */
  1626. env->line_count -= 1;
  1627. return lines;
  1628. }
  1629. /**
  1630. * Split a line into two lines at the given column
  1631. */
  1632. line_t ** split_line(line_t ** lines, int line, int split) {
  1633. /* If we're trying to split from the start, just add a new blank line before */
  1634. if (split == 0) {
  1635. return add_line(lines, line);
  1636. }
  1637. if (!env->loading && global_config.history_enabled) {
  1638. history_t * e = malloc(sizeof(history_t));
  1639. e->type = HISTORY_SPLIT_LINE;
  1640. e->contents.add_merge_split_lines.lineno = line;
  1641. e->contents.add_merge_split_lines.split = split;
  1642. HIST_APPEND(e);
  1643. }
  1644. /* Allocate more space as needed */
  1645. if (env->line_count == env->line_avail) {
  1646. env->line_avail *= 2;
  1647. lines = realloc(lines, sizeof(line_t *) * env->line_avail);
  1648. }
  1649. if (!env->loading) {
  1650. unhighlight_matching_paren();
  1651. }
  1652. /* Shift later lines down */
  1653. if (line < env->line_count) {
  1654. memmove(&lines[line+2], &lines[line+1], sizeof(line_t *) * (env->line_count - line));
  1655. }
  1656. /* I have no idea what this is doing */
  1657. int remaining = lines[line]->actual - split;
  1658. int v = remaining;
  1659. v--;
  1660. v |= v >> 1;
  1661. v |= v >> 2;
  1662. v |= v >> 4;
  1663. v |= v >> 8;
  1664. v |= v >> 16;
  1665. v++;
  1666. /* Allocate space for the new line */
  1667. lines[line+1] = calloc(sizeof(line_t) + sizeof(char_t) * v, 1);
  1668. lines[line+1]->available = v;
  1669. lines[line+1]->actual = remaining;
  1670. /* Move the data from the old line into the new line */
  1671. memmove(lines[line+1]->text, &lines[line]->text[split], sizeof(char_t) * remaining);
  1672. lines[line]->actual = split;
  1673. if (!env->loading) {
  1674. lines[line]->rev_status = 2;
  1675. lines[line+1]->rev_status = 2;
  1676. recalculate_tabs(lines[line]);
  1677. recalculate_tabs(lines[line+1]);
  1678. recalculate_syntax(lines[line], line);
  1679. recalculate_syntax(lines[line+1], line+1);
  1680. }
  1681. /* There is one new line */
  1682. env->line_count += 1;
  1683. /* We may have reallocated lines */
  1684. return lines;
  1685. }
  1686. /**
  1687. * Understand spaces and comments and check if the previous line
  1688. * ended with a brace or a colon.
  1689. */
  1690. int line_ends_with_brace(line_t * line) {
  1691. int i = line->actual-1;
  1692. while (i >= 0) {
  1693. if ((line->text[i].flags & 0x1F) == FLAG_COMMENT || line->text[i].codepoint == ' ') {
  1694. i--;
  1695. } else {
  1696. break;
  1697. }
  1698. }
  1699. if (i < 0) return 0;
  1700. return (line->text[i].codepoint == '{' || line->text[i].codepoint == ':') ? i+1 : 0;
  1701. }
  1702. int line_is_comment(line_t * line) {
  1703. if (!env->syntax) return 0;
  1704. if (!strcmp(env->syntax->name,"c")) {
  1705. if (line->istate == 1) return 1;
  1706. } else if (!strcmp(env->syntax->name,"java")) {
  1707. if (line->istate == 1) return 1;
  1708. } else if (!strcmp(env->syntax->name,"kotlin")) {
  1709. if (line->istate == 1) return 1;
  1710. } else if (!strcmp(env->syntax->name,"rust")) {
  1711. if (line->istate > 0) return 1;
  1712. }
  1713. return 0;
  1714. }
  1715. int find_brace_line_start(int line, int col) {
  1716. int ncol = col - 1;
  1717. while (ncol > 0) {
  1718. if (env->lines[line-1]->text[ncol-1].codepoint == ')') {
  1719. int t_line_no = env->line_no;
  1720. int t_col_no = env->col_no;
  1721. env->line_no = line;
  1722. env->col_no = ncol;
  1723. int paren_match_line = -1, paren_match_col = -1;
  1724. find_matching_paren(&paren_match_line, &paren_match_col, 1);
  1725. if (paren_match_line != -1) {
  1726. line = paren_match_line;
  1727. }
  1728. env->line_no = t_line_no;
  1729. env->col_no = t_col_no;
  1730. break;
  1731. } else if (env->lines[line-1]->text[ncol-1].codepoint == ' ') {
  1732. ncol--;
  1733. } else {
  1734. break;
  1735. }
  1736. }
  1737. return line;
  1738. }
  1739. /**
  1740. * Add indentation from the previous (temporally) line
  1741. */
  1742. void add_indent(int new_line, int old_line, int ignore_brace) {
  1743. if (env->indent) {
  1744. int changed = 0;
  1745. if (old_line < new_line && line_is_comment(env->lines[new_line])) {
  1746. for (int i = 0; i < env->lines[old_line]->actual; ++i) {
  1747. if (env->lines[old_line]->text[i].codepoint == '/') {
  1748. if (env->lines[old_line]->text[i+1].codepoint == '*') {
  1749. /* Insert ' * ' */
  1750. char_t space = {1,FLAG_COMMENT,' '};
  1751. char_t asterisk = {1,FLAG_COMMENT,'*'};
  1752. env->lines[new_line] = line_insert(env->lines[new_line],space,i,new_line);
  1753. env->lines[new_line] = line_insert(env->lines[new_line],asterisk,i+1,new_line);
  1754. env->lines[new_line] = line_insert(env->lines[new_line],space,i+2,new_line);
  1755. env->col_no += 3;
  1756. }
  1757. break;
  1758. } else if (env->lines[old_line]->text[i].codepoint == ' ' && env->lines[old_line]->text[i+1].codepoint == '*') {
  1759. /* Insert ' * ' */
  1760. char_t space = {1,FLAG_COMMENT,' '};
  1761. char_t asterisk = {1,FLAG_COMMENT,'*'};
  1762. env->lines[new_line] = line_insert(env->lines[new_line],space,i,new_line);
  1763. env->lines[new_line] = line_insert(env->lines[new_line],asterisk,i+1,new_line);
  1764. env->lines[new_line] = line_insert(env->lines[new_line],space,i+2,new_line);
  1765. env->col_no += 3;
  1766. break;
  1767. } else if (env->lines[old_line]->text[i].codepoint == ' ' ||
  1768. env->lines[old_line]->text[i].codepoint == '\t' ||
  1769. env->lines[old_line]->text[i].codepoint == '*') {
  1770. env->lines[new_line] = line_insert(env->lines[new_line],env->lines[old_line]->text[i],i,new_line);
  1771. env->col_no++;
  1772. changed = 1;
  1773. } else {
  1774. break;
  1775. }
  1776. }
  1777. } else {
  1778. int line_to_copy_from = old_line;
  1779. int col;
  1780. if (old_line < new_line &&
  1781. !ignore_brace &&
  1782. (col = line_ends_with_brace(env->lines[old_line])) &&
  1783. env->lines[old_line]->text[col-1].codepoint == '{') {
  1784. line_to_copy_from = find_brace_line_start(old_line+1, col)-1;
  1785. }
  1786. for (int i = 0; i < env->lines[line_to_copy_from]->actual; ++i) {
  1787. if (line_to_copy_from < new_line && i == env->lines[line_to_copy_from]->actual - 3 &&
  1788. env->lines[line_to_copy_from]->text[i].codepoint == ' ' &&
  1789. env->lines[line_to_copy_from]->text[i+1].codepoint == '*' &&
  1790. env->lines[line_to_copy_from]->text[i+2].codepoint == '/') {
  1791. break;
  1792. } else if (env->lines[line_to_copy_from]->text[i].codepoint == ' ' ||
  1793. env->lines[line_to_copy_from]->text[i].codepoint == '\t') {
  1794. env->lines[new_line] = line_insert(env->lines[new_line],env->lines[line_to_copy_from]->text[i],i,new_line);
  1795. env->col_no++;
  1796. changed = 1;
  1797. } else {
  1798. break;
  1799. }
  1800. }
  1801. }
  1802. if (old_line < new_line && !ignore_brace && line_ends_with_brace(env->lines[old_line])) {
  1803. if (env->tabs) {
  1804. char_t c;
  1805. c.codepoint = '\t';
  1806. c.display_width = env->tabstop;
  1807. env->lines[new_line] = line_insert(env->lines[new_line], c, env->col_no-1, new_line);
  1808. env->col_no++;
  1809. changed = 1;
  1810. } else {
  1811. for (int j = 0; j < env->tabstop; ++j) {
  1812. char_t c;
  1813. c.codepoint = ' ';
  1814. c.display_width = 1;
  1815. c.flags = FLAG_SELECT;
  1816. env->lines[new_line] = line_insert(env->lines[new_line], c, env->col_no-1, new_line);
  1817. env->col_no++;
  1818. }
  1819. changed = 1;
  1820. }
  1821. }
  1822. int was_whitespace = 1;
  1823. for (int i = 0; i < env->lines[old_line]->actual; ++i) {
  1824. if (env->lines[old_line]->text[i].codepoint != ' ' &&
  1825. env->lines[old_line]->text[i].codepoint != '\t') {
  1826. was_whitespace = 0;
  1827. break;
  1828. }
  1829. }
  1830. if (was_whitespace) {
  1831. while (env->lines[old_line]->actual) {
  1832. line_delete(env->lines[old_line], env->lines[old_line]->actual, old_line);
  1833. }
  1834. }
  1835. if (changed) {
  1836. recalculate_syntax(env->lines[new_line],new_line);
  1837. }
  1838. }
  1839. }
  1840. /**
  1841. * Initialize a buffer with default values
  1842. */
  1843. void setup_buffer(buffer_t * env) {
  1844. /* If this buffer was already initialized, clear out its line data */
  1845. if (env->lines) {
  1846. for (int i = 0; i < env->line_count; ++i) {
  1847. free(env->lines[i]);
  1848. }
  1849. free(env->lines);
  1850. }
  1851. /* Default state parameters */
  1852. env->line_no = 1; /* Default cursor position */
  1853. env->col_no = 1;
  1854. env->line_count = 1; /* Buffers always have at least one line */
  1855. env->modified = 0;
  1856. env->readonly = 0;
  1857. env->offset = 0;
  1858. env->line_avail = 8; /* Default line buffer capacity */
  1859. env->tabs = 1; /* Tabs by default */
  1860. env->tabstop = 4; /* Tab stop width */
  1861. env->indent = 1; /* Auto-indent by default */
  1862. env->history = malloc(sizeof(struct history));
  1863. memset(env->history, 0, sizeof(struct history));
  1864. env->last_save_history = env->history;
  1865. /* Allocate line buffer */
  1866. env->lines = malloc(sizeof(line_t *) * env->line_avail);
  1867. /* Initialize the first line */
  1868. env->lines[0] = calloc(sizeof(line_t) + sizeof(char_t) * 32, 1);
  1869. env->lines[0]->available = 32;
  1870. }
  1871. /**
  1872. * Toggle buffered / unbuffered modes
  1873. */
  1874. struct termios old;
  1875. void get_initial_termios(void) {
  1876. tcgetattr(STDOUT_FILENO, &old);
  1877. }
  1878. void set_unbuffered(void) {
  1879. struct termios new = old;
  1880. new.c_iflag &= (~ICRNL) & (~IXON);
  1881. new.c_lflag &= (~ICANON) & (~ECHO) & (~ISIG);
  1882. tcsetattr(STDOUT_FILENO, TCSAFLUSH, &new);
  1883. }
  1884. void set_buffered(void) {
  1885. tcsetattr(STDOUT_FILENO, TCSAFLUSH, &old);
  1886. }
  1887. /**
  1888. * Convert codepoint to utf-8 string
  1889. */
  1890. int to_eight(uint32_t codepoint, char * out) {
  1891. memset(out, 0x00, 7);
  1892. if (codepoint < 0x0080) {
  1893. out[0] = (char)codepoint;
  1894. } else if (codepoint < 0x0800) {
  1895. out[0] = 0xC0 | (codepoint >> 6);
  1896. out[1] = 0x80 | (codepoint & 0x3F);
  1897. } else if (codepoint < 0x10000) {
  1898. out[0] = 0xE0 | (codepoint >> 12);
  1899. out[1] = 0x80 | ((codepoint >> 6) & 0x3F);
  1900. out[2] = 0x80 | (codepoint & 0x3F);
  1901. } else if (codepoint < 0x200000) {
  1902. out[0] = 0xF0 | (codepoint >> 18);
  1903. out[1] = 0x80 | ((codepoint >> 12) & 0x3F);
  1904. out[2] = 0x80 | ((codepoint >> 6) & 0x3F);
  1905. out[3] = 0x80 | ((codepoint) & 0x3F);
  1906. } else if (codepoint < 0x4000000) {
  1907. out[0] = 0xF8 | (codepoint >> 24);
  1908. out[1] = 0x80 | (codepoint >> 18);
  1909. out[2] = 0x80 | ((codepoint >> 12) & 0x3F);
  1910. out[3] = 0x80 | ((codepoint >> 6) & 0x3F);
  1911. out[4] = 0x80 | ((codepoint) & 0x3F);
  1912. } else {
  1913. out[0] = 0xF8 | (codepoint >> 30);
  1914. out[1] = 0x80 | ((codepoint >> 24) & 0x3F);
  1915. out[2] = 0x80 | ((codepoint >> 18) & 0x3F);
  1916. out[3] = 0x80 | ((codepoint >> 12) & 0x3F);
  1917. out[4] = 0x80 | ((codepoint >> 6) & 0x3F);
  1918. out[5] = 0x80 | ((codepoint) & 0x3F);
  1919. }
  1920. return strlen(out);
  1921. }
  1922. /**
  1923. * Get the presentation width of a codepoint
  1924. */
  1925. int codepoint_width(wchar_t codepoint) {
  1926. if (codepoint == '\t') {
  1927. return 1; /* Recalculate later */
  1928. }
  1929. if (codepoint < 32) {
  1930. /* We render these as ^@ */
  1931. return 2;
  1932. }
  1933. if (codepoint == 0x7F) {
  1934. /* Renders as ^? */
  1935. return 2;
  1936. }
  1937. if (codepoint > 0x7f && codepoint < 0xa0) {
  1938. /* Upper control bytes <xx> */
  1939. return 4;
  1940. }
  1941. if (codepoint == 0xa0) {
  1942. /* Non-breaking space _ */
  1943. return 1;
  1944. }
  1945. /* Skip wcwidth for anything under 256 */
  1946. if (codepoint > 256) {
  1947. if (global_config.can_unicode) {
  1948. /* Higher codepoints may be wider (eg. Japanese) */
  1949. int out = wcwidth(codepoint);
  1950. if (out >= 1) return out;
  1951. }
  1952. /* Invalid character, render as [U+ABCD] or [U+ABCDEF] */
  1953. return (codepoint < 0x10000) ? 8 : 10;
  1954. }
  1955. return 1;
  1956. }
  1957. /**
  1958. * Move the terminal cursor
  1959. */
  1960. void place_cursor(int x, int y) {
  1961. printf("\033[%d;%dH", y, x);
  1962. }
  1963. char * color_string(const char * fg, const char * bg) {
  1964. static char output[100];
  1965. char * t = output;
  1966. t += sprintf(t,"\033[22;23;24;");
  1967. if (*bg == '@') {
  1968. int _bg = atoi(bg+1);
  1969. if (_bg < 10) {
  1970. t += sprintf(t, "4%d;", _bg);
  1971. } else {
  1972. t += sprintf(t, "10%d;", _bg-10);
  1973. }
  1974. } else {
  1975. t += sprintf(t, "48;%s;", bg);
  1976. }
  1977. if (*fg == '@') {
  1978. int _fg = atoi(fg+1);
  1979. if (_fg < 10) {
  1980. t += sprintf(t, "3%dm", _fg);
  1981. } else {
  1982. t += sprintf(t, "9%dm", _fg-10);
  1983. }
  1984. } else {
  1985. t += sprintf(t, "38;%sm", fg);
  1986. }
  1987. return output;
  1988. }
  1989. /**
  1990. * Set text colors
  1991. *
  1992. * Normally, text colors are just strings, but if they
  1993. * start with @ they will be parsed as integers
  1994. * representing one of the 16 standard colors, suitable
  1995. * for terminals without support for the 256- or 24-bit
  1996. * color modes.
  1997. */
  1998. void set_colors(const char * fg, const char * bg) {
  1999. printf("%s", color_string(fg, bg));
  2000. }
  2001. /**
  2002. * Set just the foreground color
  2003. *
  2004. * (See set_colors above)
  2005. */
  2006. void set_fg_color(const char * fg) {
  2007. printf("\033[22;23;24;");
  2008. if (*fg == '@') {
  2009. int _fg = atoi(fg+1);
  2010. if (_fg < 10) {
  2011. printf("3%dm", _fg);
  2012. } else {
  2013. printf("9%dm", _fg-10);
  2014. }
  2015. } else {
  2016. printf("38;%sm", fg);
  2017. }
  2018. }
  2019. /**
  2020. * Clear the rest of this line
  2021. */
  2022. void clear_to_end(void) {
  2023. if (global_config.can_bce) {
  2024. printf("\033[K");
  2025. }
  2026. }
  2027. /**
  2028. * For terminals without bce,
  2029. * prepaint the whole line, so we don't have to track
  2030. * where the cursor is for everything. Inefficient,
  2031. * but effective.
  2032. */
  2033. void paint_line(const char * bg) {
  2034. if (!global_config.can_bce) {
  2035. set_colors(COLOR_FG, bg);
  2036. for (int i = 0; i < global_config.term_width; ++i) {
  2037. printf(" ");
  2038. }
  2039. printf("\r");
  2040. }
  2041. }
  2042. /**
  2043. * Enable bold text display
  2044. */
  2045. void set_bold(void) {
  2046. printf("\033[1m");
  2047. }
  2048. /**
  2049. * Disable bold
  2050. */
  2051. void unset_bold(void) {
  2052. printf("\033[22m");
  2053. }
  2054. /**
  2055. * Enable underlined text display
  2056. */
  2057. void set_underline(void) {
  2058. printf("\033[4m");
  2059. }
  2060. /**
  2061. * Disable underlined text display
  2062. */
  2063. void unset_underline(void) {
  2064. printf("\033[24m");
  2065. }
  2066. /**
  2067. * Reset text display attributes
  2068. */
  2069. void reset(void) {
  2070. printf("\033[0m");
  2071. }
  2072. /**
  2073. * Clear the entire screen
  2074. */
  2075. void clear_screen(void) {
  2076. printf("\033[H\033[2J");
  2077. }
  2078. /**
  2079. * Hide the cursor
  2080. */
  2081. void hide_cursor(void) {
  2082. if (global_config.can_hideshow) {
  2083. printf("\033[?25l");
  2084. }
  2085. }
  2086. /**
  2087. * Show the cursor
  2088. */
  2089. void show_cursor(void) {
  2090. if (global_config.can_hideshow) {
  2091. printf("\033[?25h");
  2092. }
  2093. }
  2094. /**
  2095. * Store the cursor position
  2096. */
  2097. void store_cursor(void) {
  2098. printf("\0337");
  2099. }
  2100. /**
  2101. * Restore the cursor position.
  2102. */
  2103. void restore_cursor(void) {
  2104. printf("\0338");
  2105. }
  2106. /**
  2107. * Request mouse events
  2108. */
  2109. void mouse_enable(void) {
  2110. if (global_config.can_mouse) {
  2111. printf("\033[?1000h");
  2112. if (global_config.use_sgr_mouse) {
  2113. printf("\033[?1006h");
  2114. }
  2115. }
  2116. }
  2117. /**
  2118. * Stop mouse events
  2119. */
  2120. void mouse_disable(void) {
  2121. if (global_config.can_mouse) {
  2122. if (global_config.use_sgr_mouse) {
  2123. printf("\033[?1006l");
  2124. }
  2125. printf("\033[?1000l");
  2126. }
  2127. }
  2128. /**
  2129. * Shift the screen up one line
  2130. */
  2131. void shift_up(int amount) {
  2132. printf("\033[%dS", amount);
  2133. }
  2134. /**
  2135. * Shift the screen down one line.
  2136. */
  2137. void shift_down(int amount) {
  2138. printf("\033[%dT", amount);
  2139. }
  2140. void insert_lines_at(int line, int count) {
  2141. place_cursor(1, line);
  2142. printf("\033[%dL", count);
  2143. }
  2144. void delete_lines_at(int line, int count) {
  2145. place_cursor(1, line);
  2146. printf("\033[%dM", count);
  2147. }
  2148. /**
  2149. * Switch to the alternate terminal screen.
  2150. */
  2151. void set_alternate_screen(void) {
  2152. if (global_config.can_altscreen) {
  2153. printf("\033[?1049h");
  2154. }
  2155. }
  2156. /**
  2157. * Restore the standard terminal screen.
  2158. */
  2159. void unset_alternate_screen(void) {
  2160. if (global_config.can_altscreen) {
  2161. printf("\033[?1049l");
  2162. }
  2163. }
  2164. /**
  2165. * Enable bracketed paste mode.
  2166. */
  2167. void set_bracketed_paste(void) {
  2168. if (global_config.can_bracketedpaste) {
  2169. printf("\033[?2004h");
  2170. }
  2171. }
  2172. /**
  2173. * Disable bracketed paste mode.
  2174. */
  2175. void unset_bracketed_paste(void) {
  2176. if (global_config.can_bracketedpaste) {
  2177. printf("\033[?2004l");
  2178. }
  2179. }
  2180. /**
  2181. * Get the name of just a file from a full path.
  2182. * Returns a pointer within the original string.
  2183. */
  2184. char * file_basename(char * file) {
  2185. char * c = strrchr(file, '/');
  2186. if (!c) return file;
  2187. return (c+1);
  2188. }
  2189. /**
  2190. * Print a tab name with fixed width and modifiers
  2191. * into an output buffer and return the written width.
  2192. */
  2193. int draw_tab_name(buffer_t * _env, char * out, int max_width, int * width) {
  2194. uint32_t c, state = 0;
  2195. char * t = _env->file_name ? file_basename(_env->file_name) : "[No Name]";
  2196. #define ADD(c) do { \
  2197. *o = c; \
  2198. o++; \
  2199. *o = '\0'; \
  2200. bytes++; \
  2201. } while (0)
  2202. char * o = out;
  2203. *o = '\0';
  2204. int bytes = 0;
  2205. if (max_width < 2) return 1;
  2206. ADD(' ');
  2207. (*width)++;
  2208. if (_env->modified) {
  2209. if (max_width < 4) return 1;
  2210. ADD('+');
  2211. (*width)++;
  2212. ADD(' ');
  2213. (*width)++;
  2214. }
  2215. while (*t) {
  2216. if (!decode(&state, &c, (unsigned char)*t)) {
  2217. char tmp[7];
  2218. int size = to_eight(c, tmp);
  2219. if (bytes + size > 62) break;
  2220. if (*width + size >= max_width) return 1;
  2221. for (int i = 0; i < size; ++i) {
  2222. ADD(tmp[i]);
  2223. }
  2224. (*width) += codepoint_width(c);
  2225. } else if (state == UTF8_REJECT) {
  2226. state = 0;
  2227. }
  2228. t++;
  2229. }
  2230. if (max_width == *width + 1) return 1;
  2231. ADD(' ');
  2232. (*width)++;
  2233. #undef ADD
  2234. return 0;
  2235. }
  2236. /**
  2237. * Redaw the tabbar, with a tab for each buffer.
  2238. *
  2239. * The active buffer is highlighted.
  2240. */
  2241. void redraw_tabbar(void) {
  2242. if (!global_config.tabs_visible) return;
  2243. /* Hide cursor while rendering UI */
  2244. hide_cursor();
  2245. /* Move to upper left */
  2246. place_cursor(1,1);
  2247. paint_line(COLOR_TABBAR_BG);
  2248. /* For each buffer... */
  2249. int offset = 0;
  2250. if (global_config.tab_offset) {
  2251. set_colors(COLOR_NUMBER_FG, COLOR_NUMBER_BG);
  2252. printf("<");
  2253. offset++;
  2254. }
  2255. for (int i = global_config.tab_offset; i < buffers_len; i++) {
  2256. buffer_t * _env = buffers[i];
  2257. if (_env == env) {
  2258. /* If this is the active buffer, highlight it */
  2259. reset();
  2260. set_colors(COLOR_FG, COLOR_BG);
  2261. set_bold();
  2262. } else {
  2263. /* Otherwise use default tab color */
  2264. reset();
  2265. set_colors(COLOR_FG, COLOR_TAB_BG);
  2266. set_underline();
  2267. }
  2268. char title[64];
  2269. int size = 0;
  2270. int filled = draw_tab_name(_env, title, global_config.term_width - offset, &size);
  2271. if (filled) {
  2272. offset += size;
  2273. printf("%s", title);
  2274. set_colors(COLOR_NUMBER_FG, COLOR_NUMBER_BG);
  2275. while (offset != global_config.term_width - 1) {
  2276. printf(" ");
  2277. offset++;
  2278. }
  2279. printf(">");
  2280. break;
  2281. }
  2282. printf("%s", title);
  2283. offset += size;
  2284. }
  2285. /* Reset bold/underline */
  2286. reset();
  2287. /* Fill the rest of the tab bar */
  2288. set_colors(COLOR_FG, COLOR_TABBAR_BG);
  2289. clear_to_end();
  2290. }
  2291. /**
  2292. * Braindead log10 implementation for the line numbers
  2293. */
  2294. int log_base_10(unsigned int v) {
  2295. int r = (v >= 1000000000) ? 9 : (v >= 100000000) ? 8 : (v >= 10000000) ? 7 :
  2296. (v >= 1000000) ? 6 : (v >= 100000) ? 5 : (v >= 10000) ? 4 :
  2297. (v >= 1000) ? 3 : (v >= 100) ? 2 : (v >= 10) ? 1 : 0;
  2298. return r;
  2299. }
  2300. /**
  2301. * Render a line of text
  2302. *
  2303. * This handles rendering the actual text content. A full line of text
  2304. * also includes a line number and some padding.
  2305. *
  2306. * width: width of the text display region (term width - line number width)
  2307. * offset: how many cells into the line to start rendering at
  2308. */
  2309. void render_line(line_t * line, int width, int offset, int line_no) {
  2310. int i = 0; /* Offset in char_t line data entries */
  2311. int j = 0; /* Offset in terminal cells */
  2312. const char * last_color = NULL;
  2313. int was_selecting = 0, was_searching = 0;
  2314. /* Set default text colors */
  2315. set_colors(COLOR_FG, line->is_current ? COLOR_ALT_BG : COLOR_BG);
  2316. /*
  2317. * When we are rendering in the middle of a wide character,
  2318. * we render -'s to fill the remaining amount of the
  2319. * charater's width
  2320. */
  2321. int remainder = 0;
  2322. int is_spaces = 1;
  2323. /* For each character in the line ... */
  2324. while (i < line->actual) {
  2325. /* If there is remaining text... */
  2326. if (remainder) {
  2327. /* If we should be drawing by now... */
  2328. if (j >= offset) {
  2329. /* Fill remainder with -'s */
  2330. set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2331. printf("-");
  2332. set_colors(COLOR_FG, line->is_current ? COLOR_ALT_BG : COLOR_BG);
  2333. }
  2334. /* One less remaining width cell to fill */
  2335. remainder--;
  2336. /* Terminal offset moves forward */
  2337. j++;
  2338. /*
  2339. * If this was the last remaining character, move to
  2340. * the next codepoint in the line
  2341. */
  2342. if (remainder == 0) {
  2343. i++;
  2344. }
  2345. continue;
  2346. }
  2347. /* Get the next character to draw */
  2348. char_t c = line->text[i];
  2349. if (c.codepoint != ' ') is_spaces = 0;
  2350. /* If we should be drawing by now... */
  2351. if (j >= offset) {
  2352. /* If this character is going to fall off the edge of the screen... */
  2353. if (j - offset + c.display_width >= width) {
  2354. /* We draw this with special colors so it isn't ambiguous */
  2355. set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2356. /* If it's wide, draw ---> as needed */
  2357. while (j - offset < width - 1) {
  2358. printf("-");
  2359. j++;
  2360. }
  2361. /* End the line with a > to show it overflows */
  2362. printf(">");
  2363. set_colors(COLOR_FG, COLOR_BG);
  2364. return;
  2365. }
  2366. /* Syntax highlighting */
  2367. const char * color = flag_to_color(c.flags);
  2368. if (c.flags & FLAG_SELECT) {
  2369. set_colors(COLOR_SELECTFG, COLOR_SELECTBG);
  2370. was_selecting = 1;
  2371. } else if ((c.flags & FLAG_SEARCH) || (c.flags == FLAG_NOTICE)) {
  2372. set_colors(COLOR_SEARCH_FG, COLOR_SEARCH_BG);
  2373. was_searching = 1;
  2374. } else if (c.flags == FLAG_ERROR) {
  2375. set_colors(COLOR_ERROR_FG, COLOR_ERROR_BG);
  2376. was_searching = 1; /* co-opting this should work... */
  2377. } else {
  2378. if (was_selecting || was_searching) {
  2379. was_selecting = 0;
  2380. was_searching = 0;
  2381. set_colors(color, line->is_current ? COLOR_ALT_BG : COLOR_BG);
  2382. last_color = color;
  2383. } else if (!last_color || strcmp(color, last_color)) {
  2384. set_fg_color(color);
  2385. last_color = color;
  2386. }
  2387. }
  2388. if ((env->mode == MODE_COL_SELECTION || env->mode == MODE_COL_INSERT) &&
  2389. line_no >= ((env->start_line < env->line_no) ? env->start_line : env->line_no) &&
  2390. line_no <= ((env->start_line < env->line_no) ? env->line_no : env->start_line) &&
  2391. ((j == env->sel_col) ||
  2392. (j < env->sel_col && j + c.display_width > env->sel_col))) {
  2393. set_colors(COLOR_SELECTFG, COLOR_SELECTBG);
  2394. was_selecting = 1;
  2395. }
  2396. #define _set_colors(fg,bg) \
  2397. if (!(c.flags & FLAG_SELECT) && !(c.flags & FLAG_SEARCH) && !(was_selecting)) { \
  2398. set_colors(fg,(line->is_current && bg == COLOR_BG) ? COLOR_ALT_BG : bg); \
  2399. }
  2400. /* Render special characters */
  2401. if (c.codepoint == '\t') {
  2402. _set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2403. printf("%s", global_config.tab_indicator);
  2404. for (int i = 1; i < c.display_width; ++i) {
  2405. printf("%s" ,global_config.space_indicator);
  2406. }
  2407. _set_colors(last_color ? last_color : COLOR_FG, COLOR_BG);
  2408. } else if (c.codepoint < 32) {
  2409. /* Codepoints under 32 to get converted to ^@ escapes */
  2410. _set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2411. printf("^%c", '@' + c.codepoint);
  2412. _set_colors(last_color ? last_color : COLOR_FG, COLOR_BG);
  2413. } else if (c.codepoint == 0x7f) {
  2414. _set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2415. printf("^?");
  2416. _set_colors(last_color ? last_color : COLOR_FG, COLOR_BG);
  2417. } else if (c.codepoint > 0x7f && c.codepoint < 0xa0) {
  2418. _set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2419. printf("<%2x>", c.codepoint);
  2420. _set_colors(last_color ? last_color : COLOR_FG, COLOR_BG);
  2421. } else if (c.codepoint == 0xa0) {
  2422. _set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2423. printf("_");
  2424. _set_colors(last_color ? last_color : COLOR_FG, COLOR_BG);
  2425. } else if (c.display_width == 8) {
  2426. _set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2427. printf("[U+%04x]", c.codepoint);
  2428. _set_colors(last_color ? last_color : COLOR_FG, COLOR_BG);
  2429. } else if (c.display_width == 10) {
  2430. _set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2431. printf("[U+%06x]", c.codepoint);
  2432. _set_colors(last_color ? last_color : COLOR_FG, COLOR_BG);
  2433. } else if (i > 0 && is_spaces && c.codepoint == ' ' && !(i % env->tabstop)) {
  2434. _set_colors(COLOR_ALT_FG, COLOR_BG); /* Normal background so this is more subtle */
  2435. if (global_config.can_unicode) {
  2436. printf("▏");
  2437. } else {
  2438. printf("|");
  2439. }
  2440. _set_colors(last_color ? last_color : COLOR_FG, COLOR_BG);
  2441. } else if (c.codepoint == ' ' && i == line->actual - 1) {
  2442. /* Special case: space at end of line */
  2443. _set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2444. printf("%s",global_config.space_indicator);
  2445. _set_colors(COLOR_FG, COLOR_BG);
  2446. } else {
  2447. /* Normal characters get output */
  2448. char tmp[7]; /* Max six bytes, use 7 to ensure last is always nil */
  2449. to_eight(c.codepoint, tmp);
  2450. printf("%s", tmp);
  2451. }
  2452. /* Advance the terminal cell offset by the render width of this character */
  2453. j += c.display_width;
  2454. /* Advance to the next character */
  2455. i++;
  2456. } else if (c.display_width > 1) {
  2457. /*
  2458. * If this is a wide character but we aren't ready to render yet,
  2459. * we may need to draw some filler text for the remainder of its
  2460. * width to ensure we don't jump around when horizontally scrolling
  2461. * past wide characters.
  2462. */
  2463. remainder = c.display_width - 1;
  2464. j++;
  2465. } else {
  2466. /* Regular character, not ready to draw, advance without doing anything */
  2467. j++;
  2468. i++;
  2469. }
  2470. }
  2471. if (env->mode != MODE_LINE_SELECTION) {
  2472. if (line->is_current) {
  2473. set_colors(COLOR_FG, COLOR_ALT_BG);
  2474. } else {
  2475. set_colors(COLOR_FG, COLOR_BG);
  2476. }
  2477. } else {
  2478. if (!line->actual) {
  2479. if (env->line_no == line_no ||
  2480. (env->start_line > env->line_no &&
  2481. (line_no >= env->line_no && line_no <= env->start_line)) ||
  2482. (env->start_line < env->line_no &&
  2483. (line_no >= env->start_line && line_no <= env->line_no))) {
  2484. set_colors(COLOR_SELECTFG, COLOR_SELECTBG);
  2485. }
  2486. }
  2487. }
  2488. if ((env->mode == MODE_COL_SELECTION || env->mode == MODE_COL_INSERT) &&
  2489. line_no >= ((env->start_line < env->line_no) ? env->start_line : env->line_no) &&
  2490. line_no <= ((env->start_line < env->line_no) ? env->line_no : env->start_line) &&
  2491. j <= env->sel_col &&
  2492. env->sel_col < width) {
  2493. set_colors(COLOR_FG, COLOR_BG);
  2494. while (j < env->sel_col) {
  2495. printf(" ");
  2496. j++;
  2497. }
  2498. set_colors(COLOR_SELECTFG, COLOR_SELECTBG);
  2499. printf(" ");
  2500. j++;
  2501. set_colors(COLOR_FG, COLOR_BG);
  2502. }
  2503. if (env->maxcolumn && line_no > -1 /* ensures we don't do this for command line */) {
  2504. /* Fill out the normal background */
  2505. if (j < offset) j = offset;
  2506. for (; j < width + offset && j < env->maxcolumn; ++j) {
  2507. printf(" ");
  2508. }
  2509. /* Draw the line */
  2510. if (j < width + offset && j == env->maxcolumn) {
  2511. j++;
  2512. set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2513. if (global_config.can_unicode) {
  2514. printf("▏");
  2515. } else {
  2516. printf("|");
  2517. }
  2518. }
  2519. /* Fill the rest with the alternate background color */
  2520. set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2521. }
  2522. if (env->left + env->width == global_config.term_width && global_config.can_bce) {
  2523. clear_to_end();
  2524. } else {
  2525. /* Paint the rest of the line */
  2526. if (j < offset) j = offset;
  2527. for (; j < width + offset; ++j) {
  2528. printf(" ");
  2529. }
  2530. }
  2531. }
  2532. /**
  2533. * Get the width of the line number region
  2534. */
  2535. int num_width(void) {
  2536. if (!env->numbers) return 0;
  2537. int w = log_base_10(env->line_count) + 3;
  2538. if (w < 4) return 4;
  2539. return w;
  2540. }
  2541. /**
  2542. * Display width of the revision status gutter.
  2543. */
  2544. int gutter_width(void) {
  2545. return env->gutter;
  2546. }
  2547. /**
  2548. * Draw the gutter and line numbers.
  2549. */
  2550. void draw_line_number(int x) {
  2551. if (!env->numbers) return;
  2552. /* Draw the line number */
  2553. if (env->lines[x]->is_current) {
  2554. set_colors(COLOR_NUMBER_BG, COLOR_NUMBER_FG);
  2555. } else {
  2556. set_colors(COLOR_NUMBER_FG, COLOR_NUMBER_BG);
  2557. }
  2558. if (global_config.relative_lines && x+1 != env->line_no) {
  2559. x = x+1 - env->line_no;
  2560. x = ((x < 0) ? -x : x)-1;
  2561. }
  2562. int num_size = num_width() - 2; /* Padding */
  2563. for (int y = 0; y < num_size - log_base_10(x + 1); ++y) {
  2564. printf(" ");
  2565. }
  2566. printf("%d%c", x + 1, ((x+1 == env->line_no || global_config.horizontal_shift_scrolling) && env->coffset > 0) ? '<' : ' ');
  2567. }
  2568. /**
  2569. * Used to highlight the current line after moving the cursor.
  2570. */
  2571. void recalculate_current_line(void) {
  2572. int something_changed = 0;
  2573. if (global_config.highlight_current_line) {
  2574. for (int i = 0; i < env->line_count; ++i) {
  2575. if (env->lines[i]->is_current && i != env->line_no-1) {
  2576. env->lines[i]->is_current = 0;
  2577. something_changed = 1;
  2578. redraw_line(i);
  2579. } else if (i == env->line_no-1 && !env->lines[i]->is_current) {
  2580. env->lines[i]->is_current = 1;
  2581. something_changed = 1;
  2582. redraw_line(i);
  2583. }
  2584. }
  2585. } else {
  2586. something_changed = 1;
  2587. }
  2588. if (something_changed && global_config.relative_lines) {
  2589. for (int i = env->offset; i < env->offset + global_config.term_height - global_config.bottom_size - 1 && i < env->line_count; ++i) {
  2590. /* Place cursor for line number */
  2591. place_cursor(1 + gutter_width() + env->left, (i)-env->offset + 1 + global_config.tabs_visible);
  2592. draw_line_number(i);
  2593. }
  2594. }
  2595. }
  2596. /**
  2597. * Redraw line.
  2598. *
  2599. * This draws the line number as well as the actual text.
  2600. */
  2601. void redraw_line(int x) {
  2602. if (env->loading) return;
  2603. /* Determine if this line is visible. */
  2604. if (x - env->offset < 0 || x - env->offset > global_config.term_height - global_config.bottom_size - 1 - global_config.tabs_visible) {
  2605. return;
  2606. }
  2607. /* Calculate offset in screen */
  2608. int j = x - env->offset;
  2609. /* Hide cursor when drawing */
  2610. hide_cursor();
  2611. /* Move cursor to upper left most cell of this line */
  2612. place_cursor(1 + env->left,1 + global_config.tabs_visible + j);
  2613. /* Draw a gutter on the left. */
  2614. if (env->gutter) {
  2615. switch (env->lines[x]->rev_status) {
  2616. case 1:
  2617. set_colors(COLOR_NUMBER_FG, COLOR_GREEN);
  2618. printf(" ");
  2619. break;
  2620. case 2:
  2621. set_colors(COLOR_NUMBER_FG, global_config.color_gutter ? COLOR_SEARCH_BG : COLOR_ALT_FG);
  2622. printf(" ");
  2623. break;
  2624. case 3:
  2625. set_colors(COLOR_NUMBER_FG, COLOR_KEYWORD);
  2626. printf(" ");
  2627. break;
  2628. case 4:
  2629. set_colors(COLOR_ALT_FG, COLOR_RED);
  2630. printf("▆");
  2631. break;
  2632. case 5:
  2633. set_colors(COLOR_KEYWORD, COLOR_RED);
  2634. printf("▆");
  2635. break;
  2636. default:
  2637. set_colors(COLOR_NUMBER_FG, COLOR_ALT_FG);
  2638. printf(" ");
  2639. break;
  2640. }
  2641. }
  2642. draw_line_number(x);
  2643. /*
  2644. * Draw the line text
  2645. * If this is the active line, the current character cell offset should be used.
  2646. * (Non-active lines are not shifted and always render from the start of the line)
  2647. */
  2648. render_line(env->lines[x], env->width - gutter_width() - num_width(), (x + 1 == env->line_no || global_config.horizontal_shift_scrolling) ? env->coffset : 0, x+1);
  2649. }
  2650. /**
  2651. * Draw a ~ line where there is no buffer text.
  2652. */
  2653. void draw_excess_line(int j) {
  2654. place_cursor(1+env->left,1 + global_config.tabs_visible + j);
  2655. paint_line(COLOR_ALT_BG);
  2656. set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  2657. printf("~");
  2658. if (env->left + env->width == global_config.term_width && global_config.can_bce) {
  2659. clear_to_end();
  2660. } else {
  2661. /* Paint the rest of the line */
  2662. for (int x = 1; x < env->width; ++x) {
  2663. printf(" ");
  2664. }
  2665. }
  2666. }
  2667. /**
  2668. * Redraw the entire text area
  2669. */
  2670. void redraw_text(void) {
  2671. if (!env) return;
  2672. if (!global_config.has_terminal) return;
  2673. /* Hide cursor while rendering */
  2674. hide_cursor();
  2675. /* Figure out the available size of the text region */
  2676. int l = global_config.term_height - global_config.bottom_size - global_config.tabs_visible;
  2677. int j = 0;
  2678. /* Draw each line */
  2679. for (int x = env->offset; j < l && x < env->line_count; x++) {
  2680. redraw_line(x);
  2681. j++;
  2682. }
  2683. /* Draw the rest of the text region as ~ lines */
  2684. for (; j < l; ++j) {
  2685. draw_excess_line(j);
  2686. }
  2687. }
  2688. static int view_left_offset = 0;
  2689. static int view_right_offset = 0;
  2690. /**
  2691. * When in split view, draw the other buffer.
  2692. * Has special handling for when the split is
  2693. * on a single buffer.
  2694. */
  2695. void redraw_alt_buffer(buffer_t * buf) {
  2696. if (left_buffer == right_buffer) {
  2697. /* draw the opposite view */
  2698. int left, width, offset;
  2699. left = env->left;
  2700. width = env->width;
  2701. offset = env->offset;
  2702. if (left == 0) {
  2703. /* Draw the right side */
  2704. env->left = width;
  2705. env->width = global_config.term_width - width;
  2706. env->offset = view_right_offset;
  2707. view_left_offset = offset;
  2708. } else {
  2709. env->left = 0;
  2710. env->width = global_config.term_width * global_config.split_percent / 100;
  2711. env->offset = view_left_offset;
  2712. view_right_offset = offset;
  2713. }
  2714. redraw_text();
  2715. env->left = left;
  2716. env->width = width;
  2717. env->offset = offset;
  2718. }
  2719. /* Swap out active buffer */
  2720. buffer_t * tmp = env;
  2721. env = buf;
  2722. /* Redraw text */
  2723. redraw_text();
  2724. /* Return original active buffer */
  2725. env = tmp;
  2726. }
  2727. /**
  2728. * Basically wcswidth() but implemented internally using our
  2729. * own utf-8 decoder to ensure it works properly.
  2730. */
  2731. int display_width_of_string(char * str) {
  2732. uint8_t * s = (uint8_t *)str;
  2733. int out = 0;
  2734. uint32_t c, state = 0;
  2735. while (*s) {
  2736. if (!decode(&state, &c, *s)) {
  2737. out += codepoint_width(c);
  2738. } else if (state == UTF8_REJECT) {
  2739. state = 0;
  2740. }
  2741. s++;
  2742. }
  2743. return out;
  2744. }
  2745. int statusbar_append_status(int *remaining_width, char * output, char * base, ...) {
  2746. va_list args;
  2747. va_start(args, base);
  2748. char tmp[100]; /* should be big enough */
  2749. vsnprintf(tmp, 100, base, args);
  2750. va_end(args);
  2751. int width = display_width_of_string(tmp) + 2;
  2752. if (width < *remaining_width) {
  2753. strcat(output,color_string(COLOR_STATUS_ALT, COLOR_STATUS_BG));
  2754. strcat(output,"[");
  2755. strcat(output,color_string(COLOR_STATUS_FG, COLOR_STATUS_BG));
  2756. strcat(output, tmp);
  2757. strcat(output,color_string(COLOR_STATUS_ALT, COLOR_STATUS_BG));
  2758. strcat(output,"]");
  2759. (*remaining_width) -= width;
  2760. return width;
  2761. } else {
  2762. return 0;
  2763. }
  2764. }
  2765. int statusbar_build_right(char * right_hand) {
  2766. char tmp[1024];
  2767. sprintf(tmp, " Line %d/%d Col: %d ", env->line_no, env->line_count, env->col_no);
  2768. int out = display_width_of_string(tmp);
  2769. char * s = right_hand;
  2770. s += sprintf(s, "%s", color_string(COLOR_STATUS_ALT, COLOR_STATUS_BG));
  2771. s += sprintf(s, " Line ");
  2772. s += sprintf(s, "%s", color_string(COLOR_STATUS_FG, COLOR_STATUS_BG));
  2773. s += sprintf(s, "%d/%d ", env->line_no, env->line_count);
  2774. s += sprintf(s, "%s", color_string(COLOR_STATUS_ALT, COLOR_STATUS_BG));
  2775. s += sprintf(s, " Col: ");
  2776. s += sprintf(s, "%s", color_string(COLOR_STATUS_FG, COLOR_STATUS_BG));
  2777. s += sprintf(s, "%d ", env->col_no);
  2778. return out;
  2779. }
  2780. /**
  2781. * Draw the status bar
  2782. *
  2783. * The status bar shows the name of the file, whether it has modifications,
  2784. * and (in the future) what syntax highlighting mode is enabled.
  2785. *
  2786. * The right side of the tatus bar shows the line number and column.
  2787. */
  2788. void redraw_statusbar(void) {
  2789. if (global_config.hide_statusbar) return;
  2790. if (!env) return;
  2791. /* Hide cursor while rendering */
  2792. hide_cursor();
  2793. /* Move cursor to the status bar line (second from bottom */
  2794. place_cursor(1, global_config.term_height - 1);
  2795. /* Set background colors for status line */
  2796. paint_line(COLOR_STATUS_BG);
  2797. set_colors(COLOR_STATUS_FG, COLOR_STATUS_BG);
  2798. /* Pre-render the right hand side of the status bar */
  2799. char right_hand[1024];
  2800. int right_width = statusbar_build_right(right_hand);
  2801. char status_bits[1024] = {0}; /* Sane maximum */
  2802. int status_bits_width = 0;
  2803. int remaining_width = global_config.term_width - right_width;
  2804. #define ADD(...) do { status_bits_width += statusbar_append_status(&remaining_width, status_bits, __VA_ARGS__); } while (0)
  2805. if (env->syntax) {
  2806. ADD("%s",env->syntax->name);
  2807. }
  2808. /* Print file status indicators */
  2809. if (env->modified) {
  2810. ADD("+");
  2811. }
  2812. if (env->readonly) {
  2813. ADD("ro");
  2814. }
  2815. if (env->crnl) {
  2816. ADD("crnl");
  2817. }
  2818. if (env->tabs) {
  2819. ADD("tabs");
  2820. } else {
  2821. ADD("spaces=%d", env->tabstop);
  2822. }
  2823. if (global_config.yanks) {
  2824. ADD("y:%ld", global_config.yank_count);
  2825. }
  2826. if (env->indent) {
  2827. ADD("indent");
  2828. }
  2829. if (global_config.smart_complete) {
  2830. ADD("complete");
  2831. }
  2832. #undef ADD
  2833. uint8_t * file_name = (uint8_t *)(env->file_name ? env->file_name : "[No Name]");
  2834. int file_name_width = display_width_of_string((char*)file_name);
  2835. if (remaining_width > 3) {
  2836. int is_chopped = 0;
  2837. while (remaining_width < file_name_width + 3) {
  2838. is_chopped = 1;
  2839. if ((*file_name & 0xc0) == 0xc0) { /* First byte of a multibyte character */
  2840. file_name++;
  2841. while ((*file_name & 0xc0) == 0x80) file_name++;
  2842. } else {
  2843. file_name++;
  2844. }
  2845. file_name_width = display_width_of_string((char*)file_name);
  2846. }
  2847. if (is_chopped) {
  2848. set_colors(COLOR_ALT_FG, COLOR_STATUS_BG);
  2849. printf("<");
  2850. }
  2851. set_colors(COLOR_STATUS_FG, COLOR_STATUS_BG);
  2852. printf("%s ", file_name);
  2853. }
  2854. printf("%s", status_bits);
  2855. /* Clear the rest of the status bar */
  2856. clear_to_end();
  2857. /* Move the cursor appropriately to draw it */
  2858. place_cursor(global_config.term_width - right_width, global_config.term_height - 1);
  2859. set_colors(COLOR_STATUS_FG, COLOR_STATUS_BG);
  2860. printf("%s",right_hand);
  2861. }
  2862. /**
  2863. * Redraw the navigation numbers on the right side of the command line
  2864. */
  2865. void redraw_nav_buffer() {
  2866. if (nav_buffer) {
  2867. store_cursor();
  2868. place_cursor(global_config.term_width - nav_buffer - 2, global_config.term_height);
  2869. printf("%s", nav_buf);
  2870. clear_to_end();
  2871. restore_cursor();
  2872. }
  2873. }
  2874. /**
  2875. * Draw the command line
  2876. *
  2877. * The command line either has input from the user (:quit, :!make, etc.)
  2878. * or shows the INSERT (or VISUAL in the future) mode name.
  2879. */
  2880. void redraw_commandline(void) {
  2881. if (!env) return;
  2882. /* Hide cursor while rendering */
  2883. hide_cursor();
  2884. /* Move cursor to the last line */
  2885. place_cursor(1, global_config.term_height);
  2886. /* Set background color */
  2887. paint_line(COLOR_BG);
  2888. set_colors(COLOR_FG, COLOR_BG);
  2889. /* If we are in an edit mode, note that. */
  2890. if (env->mode == MODE_INSERT) {
  2891. set_bold();
  2892. printf("-- INSERT --");
  2893. clear_to_end();
  2894. unset_bold();
  2895. } else if (env->mode == MODE_LINE_SELECTION) {
  2896. set_bold();
  2897. printf("-- LINE SELECTION -- (%d:%d)",
  2898. (env->start_line < env->line_no) ? env->start_line : env->line_no,
  2899. (env->start_line < env->line_no) ? env->line_no : env->start_line
  2900. );
  2901. clear_to_end();
  2902. unset_bold();
  2903. } else if (env->mode == MODE_COL_SELECTION) {
  2904. set_bold();
  2905. printf("-- COL SELECTION -- (%d:%d %d)",
  2906. (env->start_line < env->line_no) ? env->start_line : env->line_no,
  2907. (env->start_line < env->line_no) ? env->line_no : env->start_line,
  2908. (env->sel_col)
  2909. );
  2910. clear_to_end();
  2911. unset_bold();
  2912. } else if (env->mode == MODE_COL_INSERT) {
  2913. set_bold();
  2914. printf("-- COL INSERT -- (%d:%d %d)",
  2915. (env->start_line < env->line_no) ? env->start_line : env->line_no,
  2916. (env->start_line < env->line_no) ? env->line_no : env->start_line,
  2917. (env->sel_col)
  2918. );
  2919. clear_to_end();
  2920. unset_bold();
  2921. } else if (env->mode == MODE_REPLACE) {
  2922. set_bold();
  2923. printf("-- REPLACE --");
  2924. clear_to_end();
  2925. unset_bold();
  2926. } else if (env->mode == MODE_CHAR_SELECTION) {
  2927. set_bold();
  2928. printf("-- CHAR SELECTION -- ");
  2929. clear_to_end();
  2930. unset_bold();
  2931. } else if (env->mode == MODE_DIRECTORY_BROWSE) {
  2932. set_bold();
  2933. printf("-- DIRECTORY BROWSE --");
  2934. clear_to_end();
  2935. unset_bold();
  2936. } else {
  2937. clear_to_end();
  2938. }
  2939. redraw_nav_buffer();
  2940. }
  2941. /**
  2942. * Draw a message on the command line.
  2943. */
  2944. void render_commandline_message(char * message, ...) {
  2945. /* varargs setup */
  2946. va_list args;
  2947. va_start(args, message);
  2948. char buf[1024];
  2949. /* Process format string */
  2950. vsnprintf(buf, 1024, message, args);
  2951. va_end(args);
  2952. /* Hide cursor while rendering */
  2953. hide_cursor();
  2954. /* Move cursor to the last line */
  2955. place_cursor(1, global_config.term_height);
  2956. /* Set background color */
  2957. paint_line(COLOR_BG);
  2958. set_colors(COLOR_FG, COLOR_BG);
  2959. printf("%s", buf);
  2960. /* Clear the rest of the status bar */
  2961. clear_to_end();
  2962. redraw_nav_buffer();
  2963. }
  2964. BIM_ACTION(redraw_all, 0,
  2965. "Repaint the screen."
  2966. )(void) {
  2967. if (!env) return;
  2968. redraw_tabbar();
  2969. redraw_text();
  2970. if (left_buffer) {
  2971. redraw_alt_buffer(left_buffer == env ? right_buffer : left_buffer);
  2972. }
  2973. redraw_statusbar();
  2974. redraw_commandline();
  2975. if (global_config.overlay_mode == OVERLAY_MODE_COMMAND || global_config.overlay_mode == OVERLAY_MODE_SEARCH) {
  2976. render_command_input_buffer();
  2977. }
  2978. }
  2979. void pause_for_key(void) {
  2980. int c;
  2981. while ((c = bim_getch())== -1);
  2982. bim_unget(c);
  2983. redraw_all();
  2984. }
  2985. /**
  2986. * Redraw all screen elements except the other split view.
  2987. */
  2988. void redraw_most(void) {
  2989. redraw_tabbar();
  2990. redraw_text();
  2991. redraw_statusbar();
  2992. redraw_commandline();
  2993. }
  2994. /**
  2995. * Disable screen splitting.
  2996. */
  2997. void unsplit(void) {
  2998. if (left_buffer) {
  2999. left_buffer->left = 0;
  3000. left_buffer->width = global_config.term_width;
  3001. }
  3002. if (right_buffer) {
  3003. right_buffer->left = 0;
  3004. right_buffer->width = global_config.term_width;
  3005. }
  3006. left_buffer = NULL;
  3007. right_buffer = NULL;
  3008. redraw_all();
  3009. }
  3010. /**
  3011. * Update the terminal title bar
  3012. */
  3013. void update_title(void) {
  3014. if (!global_config.can_title) return;
  3015. char cwd[1024] = {'/',0};
  3016. getcwd(cwd, 1024);
  3017. for (int i = 1; i < 3; ++i) {
  3018. printf("\033]%d;%s%s (%s) - Bim\007", i, env->file_name ? env->file_name : "[No Name]", env->modified ? " +" : "", cwd);
  3019. }
  3020. }
  3021. /**
  3022. * Mark this buffer as modified and
  3023. * redraw the status and tabbar if needed.
  3024. */
  3025. void set_modified(void) {
  3026. /* If it was already marked modified, no need to do anything */
  3027. if (env->modified) return;
  3028. /* Mark as modified */
  3029. env->modified = 1;
  3030. /* Redraw some things */
  3031. update_title();
  3032. redraw_tabbar();
  3033. redraw_statusbar();
  3034. }
  3035. /**
  3036. * Draw a message on the status line
  3037. */
  3038. void render_status_message(char * message, ...) {
  3039. if (!env) return; /* Don't print when there's no active environment; this usually means a bimrc command tried to print something */
  3040. /* varargs setup */
  3041. va_list args;
  3042. va_start(args, message);
  3043. char buf[1024];
  3044. /* Process format string */
  3045. vsnprintf(buf, 1024, message, args);
  3046. va_end(args);
  3047. /* Hide cursor while rendering */
  3048. hide_cursor();
  3049. /* Move cursor to the status bar line (second from bottom */
  3050. place_cursor(1, global_config.term_height - 1);
  3051. /* Set background colors for status line */
  3052. paint_line(COLOR_STATUS_BG);
  3053. set_colors(COLOR_STATUS_FG, COLOR_STATUS_BG);
  3054. printf("%s", buf);
  3055. /* Clear the rest of the status bar */
  3056. clear_to_end();
  3057. }
  3058. /**
  3059. * Draw an errormessage to the command line.
  3060. */
  3061. void render_error(char * message, ...) {
  3062. /* varargs setup */
  3063. va_list args;
  3064. va_start(args, message);
  3065. char buf[1024];
  3066. /* Process format string */
  3067. vsnprintf(buf, 1024, message, args);
  3068. va_end(args);
  3069. if (env) {
  3070. /* Hide cursor while rendering */
  3071. hide_cursor();
  3072. /* Move cursor to the command line */
  3073. place_cursor(1, global_config.term_height);
  3074. /* Set appropriate error message colors */
  3075. set_colors(COLOR_ERROR_FG, COLOR_ERROR_BG);
  3076. /* Draw the message */
  3077. printf("%s", buf);
  3078. } else {
  3079. printf("bim: error during startup: %s\n", buf);
  3080. }
  3081. }
  3082. char * paren_pairs = "()[]{}<>";
  3083. int is_paren(int c) {
  3084. char * p = paren_pairs;
  3085. while (*p) {
  3086. if (c == *p) return 1;
  3087. p++;
  3088. }
  3089. return 0;
  3090. }
  3091. #define _rehighlight_parens() do { \
  3092. if (i < 0 || i >= env->line_count) break; \
  3093. for (int j = 0; j < env->lines[i]->actual; ++j) { \
  3094. if (i == line-1 && j == col-1) { \
  3095. env->lines[line-1]->text[col-1].flags |= FLAG_SELECT; \
  3096. continue; \
  3097. } else { \
  3098. env->lines[i]->text[j].flags &= (~FLAG_SELECT); \
  3099. } \
  3100. } \
  3101. redraw_line(i); \
  3102. } while (0)
  3103. /**
  3104. * If the config option is enabled, find the matching
  3105. * paren character and highlight it with the SELECT
  3106. * colors, clearing out other SELECT values. As we
  3107. * co-opt the SELECT flag, don't do this in selection
  3108. * modes - only in normal and insert modes.
  3109. */
  3110. void highlight_matching_paren(void) {
  3111. if (env->mode == MODE_LINE_SELECTION || env->mode == MODE_CHAR_SELECTION) return;
  3112. if (!global_config.highlight_parens) return;
  3113. int line = -1, col = -1;
  3114. if (env->line_no <= env->line_count && env->col_no <= env->lines[env->line_no-1]->actual &&
  3115. is_paren(env->lines[env->line_no-1]->text[env->col_no-1].codepoint)) {
  3116. find_matching_paren(&line, &col, 1);
  3117. } else if (env->line_no <= env->line_count && env->col_no > 1 && is_paren(env->lines[env->line_no-1]->text[env->col_no-2].codepoint)) {
  3118. find_matching_paren(&line, &col, 2);
  3119. }
  3120. if (env->highlighting_paren == -1 && line == -1) return;
  3121. if (env->highlighting_paren > 0) {
  3122. int i = env->highlighting_paren - 1;
  3123. _rehighlight_parens();
  3124. }
  3125. if (env->highlighting_paren != line && line != -1) {
  3126. int i = line - 1;
  3127. _rehighlight_parens();
  3128. }
  3129. env->highlighting_paren = line;
  3130. }
  3131. /**
  3132. * Recalculate syntax for the matched paren.
  3133. * Useful when entering selection modes.
  3134. */
  3135. void unhighlight_matching_paren(void) {
  3136. if (env->highlighting_paren > 0 && env->highlighting_paren <= env->line_count) {
  3137. for (int i = env->highlighting_paren - 1; i <= env->highlighting_paren + 1; ++i) {
  3138. if (i >= 1 && i <= env->line_count) {
  3139. recalculate_syntax(env->lines[i-1], i-1);
  3140. redraw_line(i-1);
  3141. }
  3142. }
  3143. env->highlighting_paren = -1;
  3144. }
  3145. }
  3146. /**
  3147. * Move the cursor to the appropriate location based
  3148. * on where it is in the text region.
  3149. *
  3150. * This does some additional math to set the text
  3151. * region horizontal offset.
  3152. */
  3153. void place_cursor_actual(void) {
  3154. /* Invalid positions */
  3155. if (env->line_no < 1) env->line_no = 1;
  3156. if (env->col_no < 1) env->col_no = 1;
  3157. /* Account for the left hand gutter */
  3158. int num_size = num_width() + gutter_width();
  3159. int x = num_size + 1 - env->coffset;
  3160. /* Determine where the cursor is physically */
  3161. for (int i = 0; i < env->col_no - 1; ++i) {
  3162. char_t * c = &env->lines[env->line_no-1]->text[i];
  3163. x += c->display_width;
  3164. }
  3165. /* y is a bit easier to calculate */
  3166. int y = env->line_no - env->offset + 1;
  3167. int needs_redraw = 0;
  3168. while (y < 2 + global_config.cursor_padding && env->offset > 0) {
  3169. y++;
  3170. env->offset--;
  3171. needs_redraw = 1;
  3172. }
  3173. while (y > 1 + global_config.term_height - global_config.bottom_size - global_config.cursor_padding - global_config.tabs_visible) {
  3174. y--;
  3175. env->offset++;
  3176. needs_redraw = 1;
  3177. }
  3178. if (needs_redraw) {
  3179. redraw_text();
  3180. redraw_tabbar();
  3181. redraw_statusbar();
  3182. redraw_commandline();
  3183. }
  3184. /* If the cursor has gone off screen to the right... */
  3185. if (x > env->width - 1) {
  3186. /* Adjust the offset appropriately to scroll horizontally */
  3187. int diff = x - (env->width - 1);
  3188. env->coffset += diff;
  3189. x -= diff;
  3190. redraw_text();
  3191. }
  3192. /* Same for scrolling horizontally to the left */
  3193. if (x < num_size + 1) {
  3194. int diff = (num_size + 1) - x;
  3195. env->coffset -= diff;
  3196. x += diff;
  3197. redraw_text();
  3198. }
  3199. highlight_matching_paren();
  3200. recalculate_current_line();
  3201. /* Move the actual terminal cursor */
  3202. place_cursor(x+env->left,y - !global_config.tabs_visible);
  3203. /* Show the cursor */
  3204. show_cursor();
  3205. }
  3206. /**
  3207. * If the screen is split, update the split sizes based
  3208. * on the new terminal width and the user's split_percent setting.
  3209. */
  3210. void update_split_size(void) {
  3211. if (!left_buffer) return;
  3212. if (left_buffer == right_buffer) {
  3213. if (left_buffer->left == 0) {
  3214. left_buffer->width = global_config.term_width * global_config.split_percent / 100;
  3215. } else {
  3216. right_buffer->left = global_config.term_width * global_config.split_percent / 100;
  3217. right_buffer->width = global_config.term_width - right_buffer->left;
  3218. }
  3219. return;
  3220. }
  3221. left_buffer->left = 0;
  3222. left_buffer->width = global_config.term_width * global_config.split_percent / 100;
  3223. right_buffer->left = left_buffer->width;
  3224. right_buffer->width = global_config.term_width - left_buffer->width;
  3225. }
  3226. /**
  3227. * Update screen size
  3228. */
  3229. void update_screen_size(void) {
  3230. struct winsize w;
  3231. ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
  3232. global_config.term_width = w.ws_col;
  3233. global_config.term_height = w.ws_row;
  3234. if (env) {
  3235. if (left_buffer) {
  3236. update_split_size();
  3237. } else if (env != left_buffer && env != right_buffer) {
  3238. env->width = w.ws_col;
  3239. }
  3240. }
  3241. for (int i = 0; i < buffers_len; ++i) {
  3242. if (buffers[i] != left_buffer && buffers[i] != right_buffer) {
  3243. buffers[i]->width = w.ws_col;
  3244. }
  3245. }
  3246. }
  3247. /**
  3248. * Handle terminal size changes
  3249. */
  3250. void SIGWINCH_handler(int sig) {
  3251. (void)sig;
  3252. update_screen_size();
  3253. redraw_all();
  3254. signal(SIGWINCH, SIGWINCH_handler);
  3255. }
  3256. /**
  3257. * Handle suspend
  3258. */
  3259. void SIGTSTP_handler(int sig) {
  3260. (void)sig;
  3261. mouse_disable();
  3262. set_buffered();
  3263. reset();
  3264. clear_screen();
  3265. show_cursor();
  3266. unset_bracketed_paste();
  3267. unset_alternate_screen();
  3268. fflush(stdout);
  3269. signal(SIGTSTP, SIG_DFL);
  3270. raise(SIGTSTP);
  3271. }
  3272. void SIGCONT_handler(int sig) {
  3273. (void)sig;
  3274. set_alternate_screen();
  3275. set_bracketed_paste();
  3276. set_unbuffered();
  3277. update_screen_size();
  3278. mouse_enable();
  3279. redraw_all();
  3280. update_title();
  3281. signal(SIGCONT, SIGCONT_handler);
  3282. signal(SIGTSTP, SIGTSTP_handler);
  3283. }
  3284. void try_to_center() {
  3285. int half_a_screen = (global_config.term_height - 3) / 2;
  3286. if (half_a_screen < env->line_no) {
  3287. env->offset = env->line_no - half_a_screen;
  3288. } else {
  3289. env->offset = 0;
  3290. }
  3291. }
  3292. BIM_ACTION(suspend, 0,
  3293. "Suspend bim and the rest of the job it was run in."
  3294. )(void) {
  3295. kill(0, SIGTSTP);
  3296. }
  3297. /**
  3298. * Move the cursor to a specific line.
  3299. */
  3300. BIM_ACTION(goto_line, ARG_IS_CUSTOM,
  3301. "Jump to the requested line."
  3302. )(int line) {
  3303. if (line == -1) line = env->line_count;
  3304. /* Respect file bounds */
  3305. if (line < 1) line = 1;
  3306. if (line > env->line_count) line = env->line_count;
  3307. /* Move the cursor / text region offsets */
  3308. env->coffset = 0;
  3309. env->line_no = line;
  3310. env->col_no = 1;
  3311. if (!env->loading) {
  3312. if (line > env->offset && line < env->offset + global_config.term_height - global_config.bottom_size) {
  3313. place_cursor_actual();
  3314. } else {
  3315. try_to_center();
  3316. }
  3317. redraw_most();
  3318. } else {
  3319. try_to_center();
  3320. }
  3321. }
  3322. /**
  3323. * Processs (part of) a file and add it to a buffer.
  3324. */
  3325. void add_buffer(uint8_t * buf, int size) {
  3326. for (int i = 0; i < size; ++i) {
  3327. if (!decode(&state, &codepoint_r, buf[i])) {
  3328. uint32_t c = codepoint_r;
  3329. if (c == '\n') {
  3330. if (!env->crnl && env->lines[env->line_no-1]->actual && env->lines[env->line_no-1]->text[env->lines[env->line_no-1]->actual-1].codepoint == '\r') {
  3331. env->lines[env->line_no-1]->actual--;
  3332. env->crnl = 1;
  3333. }
  3334. env->lines = add_line(env->lines, env->line_no);
  3335. env->col_no = 1;
  3336. env->line_no += 1;
  3337. } else if (env->crnl && c == '\r') {
  3338. continue;
  3339. } else {
  3340. char_t _c;
  3341. _c.codepoint = (uint32_t)c;
  3342. _c.flags = 0;
  3343. _c.display_width = codepoint_width((wchar_t)c);
  3344. line_t * line = env->lines[env->line_no - 1];
  3345. line_t * nline = line_insert(line, _c, env->col_no - 1, env->line_no-1);
  3346. if (line != nline) {
  3347. env->lines[env->line_no - 1] = nline;
  3348. }
  3349. env->col_no += 1;
  3350. }
  3351. } else if (state == UTF8_REJECT) {
  3352. state = 0;
  3353. }
  3354. }
  3355. }
  3356. /**
  3357. * Add a raw string to a buffer. Convenience wrapper
  3358. * for add_buffer for nil-terminated strings.
  3359. */
  3360. void add_string(char * string) {
  3361. add_buffer((uint8_t*)string,strlen(string));
  3362. }
  3363. int str_ends_with(const char * haystack, const char * needle) {
  3364. int i = strlen(haystack);
  3365. int j = strlen(needle);
  3366. do {
  3367. if (haystack[i] != needle[j]) return 0;
  3368. if (j == 0) return 1;
  3369. if (i == 0) return 0;
  3370. i--;
  3371. j--;
  3372. } while (1);
  3373. }
  3374. /**
  3375. * Find a syntax highlighter for the given filename.
  3376. */
  3377. struct syntax_definition * match_syntax(char * file) {
  3378. for (struct syntax_definition * s = syntaxes; syntaxes && s->name; ++s) {
  3379. for (char ** ext = s->ext; *ext; ++ext) {
  3380. if (str_ends_with(file, *ext)) return s;
  3381. }
  3382. }
  3383. return NULL;
  3384. }
  3385. /**
  3386. * Set the syntax configuration by the name of the syntax highlighter.
  3387. */
  3388. void set_syntax_by_name(const char * name) {
  3389. if (!strcmp(name,"none")) {
  3390. for (int i = 0; i < env->line_count; ++i) {
  3391. env->lines[i]->istate = -1;
  3392. for (int j = 0; j < env->lines[i]->actual; ++j) {
  3393. env->lines[i]->text[j].flags = 0;
  3394. }
  3395. }
  3396. env->syntax = NULL;
  3397. redraw_all();
  3398. return;
  3399. }
  3400. for (struct syntax_definition * s = syntaxes; syntaxes && s->name; ++s) {
  3401. if (!strcmp(name,s->name)) {
  3402. env->syntax = s;
  3403. for (int i = 0; i < env->line_count; ++i) {
  3404. env->lines[i]->istate = -1;
  3405. }
  3406. for (int i = 0; i < env->line_count; ++i) {
  3407. recalculate_syntax(env->lines[i],i);
  3408. }
  3409. redraw_all();
  3410. return;
  3411. }
  3412. }
  3413. render_error("unrecognized syntax type");
  3414. }
  3415. /**
  3416. * Check if a string is all numbers.
  3417. */
  3418. int is_all_numbers(const char * c) {
  3419. while (*c) {
  3420. if (!isdigit(*c)) return 0;
  3421. c++;
  3422. }
  3423. return 1;
  3424. }
  3425. struct file_listing {
  3426. int type;
  3427. char * filename;
  3428. };
  3429. int sort_files(const void * a, const void * b) {
  3430. struct file_listing * _a = (struct file_listing *)a;
  3431. struct file_listing * _b = (struct file_listing *)b;
  3432. if (_a->type == _b->type) {
  3433. return strcmp(_a->filename, _b->filename);
  3434. } else {
  3435. return _a->type - _b->type;
  3436. }
  3437. }
  3438. void read_directory_into_buffer(char * file) {
  3439. DIR * dirp = opendir(file);
  3440. if (!dirp) {
  3441. env->loading = 0;
  3442. return;
  3443. }
  3444. add_string("# Directory listing for `");
  3445. add_string(file);
  3446. add_string("`\n");
  3447. /* Flexible array to hold directory contents */
  3448. int available = 32;
  3449. int count = 0;
  3450. struct file_listing * files = calloc(sizeof(struct file_listing), available);
  3451. /* Read directory */
  3452. struct dirent * ent = readdir(dirp);
  3453. while (ent) {
  3454. struct stat statbuf;
  3455. char * tmp = malloc(strlen(file) + 1 + strlen(ent->d_name) + 1);
  3456. snprintf(tmp, strlen(file) + 1 + strlen(ent->d_name) + 1, "%s/%s", file, ent->d_name);
  3457. stat(tmp, &statbuf);
  3458. int type = (S_ISDIR(statbuf.st_mode)) ? 'd' : 'f';
  3459. if (count + 1 == available) {
  3460. available *= 2; \
  3461. files = realloc(files, sizeof(struct file_listing) * available); \
  3462. } \
  3463. files[count].type = type;
  3464. files[count].filename = strdup(ent->d_name);
  3465. count++;
  3466. ent = readdir(dirp);
  3467. }
  3468. closedir(dirp);
  3469. /* Sort directory entries */
  3470. qsort(files, count, sizeof(struct file_listing), sort_files);
  3471. for (int i = 0; i < count; ++i) {
  3472. add_string(files[i].type == 'd' ? "d" : "f");
  3473. add_string(" ");
  3474. add_string(files[i].filename);
  3475. add_string("\n");
  3476. free(files[i].filename);
  3477. }
  3478. free(files);
  3479. env->file_name = strdup(file);
  3480. env->syntax = find_syntax_calculator("dirent");
  3481. for (int i = 0; i < env->line_count; ++i) {
  3482. recalculate_syntax(env->lines[i],i);
  3483. }
  3484. env->readonly = 1;
  3485. env->loading = 0;
  3486. env->mode = MODE_DIRECTORY_BROWSE;
  3487. env->line_no = 1;
  3488. redraw_all();
  3489. }
  3490. BIM_ACTION(open_file_from_line, 0,
  3491. "When browsing a directory, open the file under the cursor."
  3492. )(void) {
  3493. if (env->lines[env->line_no-1]->actual < 1) return;
  3494. if (env->lines[env->line_no-1]->text[0].codepoint != 'd' &&
  3495. env->lines[env->line_no-1]->text[0].codepoint != 'f') return;
  3496. /* Collect file name */
  3497. char * tmp = malloc(strlen(env->file_name) + 1 + env->lines[env->line_no-1]->actual * 7); /* Should be enough */
  3498. memset(tmp, 0, strlen(env->file_name) + 1 + env->lines[env->line_no-1]->actual * 7);
  3499. char * t = tmp;
  3500. /* Start by copying the filename */
  3501. t += sprintf(t, "%s/", env->file_name);
  3502. /* Start from character 2 to skip d/f and space */
  3503. for (int i = 2; i < env->lines[env->line_no-1]->actual; ++i) {
  3504. t += to_eight(env->lines[env->line_no-1]->text[i].codepoint, t);
  3505. }
  3506. *t = '\0';
  3507. /* Normalize */
  3508. char tmp_path[PATH_MAX+1];
  3509. if (!realpath(tmp, tmp_path)) {
  3510. free(tmp);
  3511. return;
  3512. }
  3513. free(tmp);
  3514. /* Open file */
  3515. buffer_t * old_buffer = env;
  3516. open_file(tmp_path);
  3517. buffer_close(old_buffer);
  3518. update_title();
  3519. redraw_all();
  3520. }
  3521. int line_matches(line_t * line, char * string) {
  3522. uint32_t c, state = 0;
  3523. int i = 0;
  3524. while (*string) {
  3525. if (!decode(&state, &c, *string)) {
  3526. if (i >= line->actual) return 0;
  3527. if (line->text[i].codepoint != c) return 0;
  3528. string++;
  3529. i++;
  3530. } else if (state == UTF8_REJECT) {
  3531. state = 0;
  3532. }
  3533. }
  3534. return 1;
  3535. }
  3536. void run_onload(buffer_t * env) {
  3537. if (has_function("onload:*")) {
  3538. run_function("onload:*");
  3539. }
  3540. if (env->syntax) {
  3541. char tmp[512];
  3542. sprintf(tmp, "onload:%s", env->syntax->name);
  3543. if (has_function(tmp)) {
  3544. run_function(tmp);
  3545. }
  3546. }
  3547. }
  3548. /**
  3549. * Create a new buffer from a file.
  3550. */
  3551. void open_file(char * file) {
  3552. env = buffer_new();
  3553. env->width = global_config.term_width;
  3554. env->left = 0;
  3555. env->loading = 1;
  3556. setup_buffer(env);
  3557. FILE * f;
  3558. int init_line = -1;
  3559. if (!strcmp(file,"-")) {
  3560. /**
  3561. * Read file from stdin. stderr provides terminal input.
  3562. */
  3563. if (isatty(STDIN_FILENO)) {
  3564. if (buffers_len == 1) {
  3565. quit("stdin is a terminal and you tried to open -; not letting you do that");
  3566. }
  3567. close_buffer();
  3568. render_error("stdin is a terminal and you tried to open -; not letting you do that");
  3569. return;
  3570. }
  3571. f = stdin;
  3572. env->modified = 1;
  3573. } else {
  3574. char * l = strrchr(file, ':');
  3575. if (l && is_all_numbers(l+1)) {
  3576. *l = '\0';
  3577. l++;
  3578. init_line = atoi(l);
  3579. }
  3580. char * _file = file;
  3581. if (file[0] == '~') {
  3582. char * home = getenv("HOME");
  3583. if (home) {
  3584. _file = malloc(strlen(file) + strlen(home) + 4); /* Paranoia */
  3585. sprintf(_file, "%s%s", home, file+1);
  3586. }
  3587. }
  3588. struct stat statbuf;
  3589. if (!stat(_file, &statbuf) && S_ISDIR(statbuf.st_mode)) {
  3590. read_directory_into_buffer(_file);
  3591. if (file != _file) free(_file);
  3592. return;
  3593. }
  3594. f = fopen(_file, "r");
  3595. if (file != _file) free(_file);
  3596. env->file_name = strdup(file);
  3597. }
  3598. if (!f) {
  3599. if (global_config.highlight_on_open) {
  3600. env->syntax = match_syntax(file);
  3601. }
  3602. env->loading = 0;
  3603. if (global_config.go_to_line) {
  3604. goto_line(1);
  3605. }
  3606. if (env->syntax && env->syntax->prefers_spaces) {
  3607. env->tabs = 0;
  3608. }
  3609. run_onload(env);
  3610. return;
  3611. }
  3612. uint8_t buf[BLOCK_SIZE];
  3613. state = 0;
  3614. while (!feof(f) && !ferror(f)) {
  3615. size_t r = fread(buf, 1, BLOCK_SIZE, f);
  3616. add_buffer(buf, r);
  3617. }
  3618. if (ferror(f)) {
  3619. env->loading = 0;
  3620. return;
  3621. }
  3622. if (env->line_no && env->lines[env->line_no-1] && env->lines[env->line_no-1]->actual == 0) {
  3623. /* Remove blank line from end */
  3624. env->lines = remove_line(env->lines, env->line_no-1);
  3625. }
  3626. if (global_config.highlight_on_open) {
  3627. env->syntax = match_syntax(file);
  3628. if (!env->syntax) {
  3629. if (line_matches(env->lines[0], "<?xml")) set_syntax_by_name("xml");
  3630. else if (line_matches(env->lines[0], "<!doctype")) set_syntax_by_name("xml");
  3631. else if (line_matches(env->lines[0], "#!/usr/bin/env bash")) set_syntax_by_name("bash");
  3632. else if (line_matches(env->lines[0], "#!/bin/bash")) set_syntax_by_name("bash");
  3633. else if (line_matches(env->lines[0], "#!/bin/sh")) set_syntax_by_name("bash");
  3634. else if (line_matches(env->lines[0], "#!/usr/bin/env python")) set_syntax_by_name("py");
  3635. else if (line_matches(env->lines[0], "#!/usr/bin/env groovy")) set_syntax_by_name("groovy");
  3636. }
  3637. if (!env->syntax && global_config.syntax_fallback) {
  3638. set_syntax_by_name(global_config.syntax_fallback);
  3639. }
  3640. for (int i = 0; i < env->line_count; ++i) {
  3641. recalculate_syntax(env->lines[i],i);
  3642. }
  3643. }
  3644. /* Try to automatically figure out tabs vs. spaces */
  3645. int tabs = 0, spaces = 0;
  3646. for (int i = 0; i < env->line_count; ++i) {
  3647. if (env->lines[i]->actual > 1) { /* Make sure line has at least some text on it */
  3648. if (env->lines[i]->text[0].codepoint == '\t') tabs++;
  3649. if (env->lines[i]->text[0].codepoint == ' ' &&
  3650. env->lines[i]->text[1].codepoint == ' ') /* Ignore spaces at the start of asterisky C comments */
  3651. spaces++;
  3652. }
  3653. }
  3654. if (spaces > tabs) {
  3655. env->tabs = 0;
  3656. } else if (spaces == tabs && env->syntax) {
  3657. env->tabs = env->syntax->prefers_spaces;
  3658. }
  3659. /* TODO figure out tabstop for spaces? */
  3660. env->loading = 0;
  3661. if (global_config.check_git) {
  3662. env->checkgitstatusonwrite = 1;
  3663. git_examine(file);
  3664. }
  3665. for (int i = 0; i < env->line_count; ++i) {
  3666. recalculate_tabs(env->lines[i]);
  3667. }
  3668. if (global_config.go_to_line) {
  3669. if (init_line != -1) {
  3670. goto_line(init_line);
  3671. } else {
  3672. env->line_no = 1;
  3673. env->col_no = 1;
  3674. fetch_from_biminfo(env);
  3675. place_cursor_actual();
  3676. redraw_all();
  3677. set_preferred_column();
  3678. }
  3679. }
  3680. fclose(f);
  3681. run_onload(env);
  3682. }
  3683. /**
  3684. * Clean up the terminal and exit the editor.
  3685. */
  3686. void quit(const char * message) {
  3687. mouse_disable();
  3688. set_buffered();
  3689. reset();
  3690. clear_screen();
  3691. show_cursor();
  3692. unset_bracketed_paste();
  3693. unset_alternate_screen();
  3694. if (message) {
  3695. fprintf(stdout, "%s\n", message);
  3696. }
  3697. exit(0);
  3698. }
  3699. /**
  3700. * Try to quit, but don't continue if there are
  3701. * modified buffers open.
  3702. */
  3703. void try_quit(void) {
  3704. for (int i = 0; i < buffers_len; i++ ) {
  3705. buffer_t * _env = buffers[i];
  3706. if (_env->modified) {
  3707. if (_env->file_name) {
  3708. render_error("Modifications made to file `%s` in tab %d. Aborting.", _env->file_name, i+1);
  3709. } else {
  3710. render_error("Unsaved new file in tab %d. Aborting.", i+1);
  3711. }
  3712. return;
  3713. }
  3714. }
  3715. /* Close all buffers */
  3716. while (buffers_len) {
  3717. buffer_close(buffers[0]);
  3718. }
  3719. quit(NULL);
  3720. }
  3721. /**
  3722. * Switch to the previous buffer
  3723. */
  3724. BIM_ACTION(previous_tab, 0,
  3725. "Switch the previoius tab"
  3726. )(void) {
  3727. buffer_t * last = NULL;
  3728. for (int i = 0; i < buffers_len; i++) {
  3729. buffer_t * _env = buffers[i];
  3730. if (_env == env) {
  3731. if (last) {
  3732. /* Wrap around */
  3733. env = last;
  3734. if (left_buffer && (left_buffer != env && right_buffer != env)) unsplit();
  3735. redraw_all();
  3736. update_title();
  3737. return;
  3738. } else {
  3739. env = buffers[buffers_len-1];
  3740. if (left_buffer && (left_buffer != env && right_buffer != env)) unsplit();
  3741. redraw_all();
  3742. update_title();
  3743. return;
  3744. }
  3745. }
  3746. last = _env;
  3747. }
  3748. }
  3749. /**
  3750. * Switch to the next buffer
  3751. */
  3752. BIM_ACTION(next_tab, 0,
  3753. "Switch to the next tab"
  3754. )(void) {
  3755. for (int i = 0; i < buffers_len; i++) {
  3756. buffer_t * _env = buffers[i];
  3757. if (_env == env) {
  3758. if (i != buffers_len - 1) {
  3759. env = buffers[i+1];
  3760. if (left_buffer && (left_buffer != env && right_buffer != env)) unsplit();
  3761. redraw_all();
  3762. update_title();
  3763. return;
  3764. } else {
  3765. /* Wrap around */
  3766. env = buffers[0];
  3767. if (left_buffer && (left_buffer != env && right_buffer != env)) unsplit();
  3768. redraw_all();
  3769. update_title();
  3770. return;
  3771. }
  3772. }
  3773. }
  3774. }
  3775. /**
  3776. * Check for modified lines in a file by examining `git diff` output.
  3777. * This can be enabled globally in bimrc or per environment with the 'git' option.
  3778. */
  3779. int git_examine(char * filename) {
  3780. if (env->modified) return 1;
  3781. int fds[2];
  3782. pipe(fds);
  3783. int child = fork();
  3784. if (child == 0) {
  3785. FILE * dev_null = fopen("/dev/null","w");
  3786. close(fds[0]);
  3787. dup2(fds[1], STDOUT_FILENO);
  3788. dup2(fileno(dev_null), STDERR_FILENO);
  3789. char * args[] = {"git","--no-pager","diff","-U0","--no-color","--",filename,NULL};
  3790. exit(execvp("git",args));
  3791. } else if (child < 0) {
  3792. return 1;
  3793. }
  3794. close(fds[1]);
  3795. FILE * f = fdopen(fds[0],"r");
  3796. int line_offset = 0;
  3797. while (!feof(f)) {
  3798. int c = fgetc(f);
  3799. if (c < 0) break;
  3800. if (c == '@' && line_offset == 0) {
  3801. /* Read line offset, count */
  3802. if (fgetc(f) == '@' && fgetc(f) == ' ' && fgetc(f) == '-') {
  3803. /* This algorithm is borrowed from Kakoune and only requires us to parse the @@ line */
  3804. int from_line = 0;
  3805. int from_count = 0;
  3806. int to_line = 0;
  3807. int to_count = 0;
  3808. fscanf(f,"%d",&from_line);
  3809. if (fgetc(f) == ',') {
  3810. fscanf(f,"%d",&from_count);
  3811. } else {
  3812. from_count = 1;
  3813. }
  3814. fscanf(f,"%d",&to_line);
  3815. if (fgetc(f) == ',') {
  3816. fscanf(f,"%d",&to_count);
  3817. } else {
  3818. to_count = 1;
  3819. }
  3820. if (to_line > env->line_count) continue;
  3821. if (from_count == 0 && to_count > 0) {
  3822. /* No -, all + means all of to_count is green */
  3823. for (int i = 0; i < to_count; ++i) {
  3824. env->lines[to_line+i-1]->rev_status = 1; /* Green */
  3825. }
  3826. } else if (from_count > 0 && to_count == 0) {
  3827. /*
  3828. * No +, all - means we have a deletion. We mark the next line such that it has a red bar at the top
  3829. * Note that to_line is one lower than the affacted line, so we don't need to mes with indexes.
  3830. */
  3831. if (to_line >= env->line_count) continue;
  3832. env->lines[to_line]->rev_status = 4; /* Red */
  3833. } else if (from_count > 0 && from_count == to_count) {
  3834. /* from = to, all modified */
  3835. for (int i = 0; i < to_count; ++i) {
  3836. env->lines[to_line+i-1]->rev_status = 3; /* Blue */
  3837. }
  3838. } else if (from_count > 0 && from_count < to_count) {
  3839. /* from < to, some modified, some added */
  3840. for (int i = 0; i < from_count; ++i) {
  3841. env->lines[to_line+i-1]->rev_status = 3; /* Blue */
  3842. }
  3843. for (int i = from_count; i < to_count; ++i) {
  3844. env->lines[to_line+i-1]->rev_status = 1; /* Green */
  3845. }
  3846. } else if (to_count > 0 && from_count > to_count) {
  3847. /* from > to, we deleted but also modified some lines */
  3848. env->lines[to_line-1]->rev_status = 5; /* Red + Blue */
  3849. for (int i = 1; i < to_count; ++i) {
  3850. env->lines[to_line+i-1]->rev_status = 3; /* Blue */
  3851. }
  3852. }
  3853. }
  3854. }
  3855. if (c == '\n') {
  3856. line_offset = 0;
  3857. continue;
  3858. }
  3859. line_offset++;
  3860. }
  3861. fclose(f);
  3862. return 0;
  3863. }
  3864. /**
  3865. * Write file contents to FILE
  3866. */
  3867. void output_file(buffer_t * env, FILE * f) {
  3868. int i, j;
  3869. for (i = 0; i < env->line_count; ++i) {
  3870. line_t * line = env->lines[i];
  3871. line->rev_status = 0;
  3872. for (j = 0; j < line->actual; j++) {
  3873. char_t c = line->text[j];
  3874. if (c.codepoint == 0) {
  3875. char buf[1] = {0};
  3876. fwrite(buf, 1, 1, f);
  3877. } else {
  3878. char tmp[8] = {0};
  3879. int i = to_eight(c.codepoint, tmp);
  3880. fwrite(tmp, i, 1, f);
  3881. }
  3882. }
  3883. if (env->crnl) fputc('\r', f);
  3884. fputc('\n', f);
  3885. }
  3886. }
  3887. /**
  3888. * Write active buffer to file
  3889. */
  3890. void write_file(char * file) {
  3891. if (!file) {
  3892. render_error("Need a file to write to.");
  3893. return;
  3894. }
  3895. FILE * f = fopen(file, "w+");
  3896. if (!f) {
  3897. render_error("Failed to open file for writing.");
  3898. return;
  3899. }
  3900. /* Go through each line and convert it back to UTF-8 */
  3901. output_file(env, f);
  3902. fclose(f);
  3903. /* Mark it no longer modified */
  3904. env->modified = 0;
  3905. env->last_save_history = env->history;
  3906. /* If there was no file name set, set one */
  3907. if (!env->file_name) {
  3908. env->file_name = malloc(strlen(file) + 1);
  3909. memcpy(env->file_name, file, strlen(file) + 1);
  3910. }
  3911. if (env->checkgitstatusonwrite) {
  3912. git_examine(file);
  3913. }
  3914. update_title();
  3915. redraw_all();
  3916. }
  3917. /**
  3918. * Close the active buffer
  3919. */
  3920. void close_buffer(void) {
  3921. buffer_t * previous_env = env;
  3922. buffer_t * new_env = buffer_close(env);
  3923. if (new_env == previous_env) {
  3924. /* ?? Failed to close buffer */
  3925. render_error("lolwat");
  3926. }
  3927. if (left_buffer && env == left_buffer) {
  3928. left_buffer = NULL;
  3929. right_buffer->left = 0;
  3930. right_buffer->width = global_config.term_width;
  3931. right_buffer = NULL;
  3932. } else if (left_buffer && env == right_buffer) {
  3933. right_buffer = NULL;
  3934. left_buffer->left = 0;
  3935. left_buffer->width = global_config.term_width;
  3936. left_buffer = NULL;
  3937. }
  3938. /* No more buffers, exit */
  3939. if (!new_env) {
  3940. quit(NULL);
  3941. }
  3942. /* Set the new active buffer */
  3943. env = new_env;
  3944. /* Redraw the screen */
  3945. redraw_all();
  3946. update_title();
  3947. }
  3948. /**
  3949. * Set the visual column the cursor should attempt to keep
  3950. * when moving up and down based on where the cursor currently is.
  3951. * This should happen whenever the user intentionally changes
  3952. * the cursor's horizontal positioning, such as with left/right
  3953. * arrow keys, word-move, search, mouse, etc.
  3954. */
  3955. void set_preferred_column(void) {
  3956. int c = 0;
  3957. for (int i = 0; i < env->lines[env->line_no-1]->actual && i < env->col_no-1; ++i) {
  3958. c += env->lines[env->line_no-1]->text[i].display_width;
  3959. }
  3960. env->preferred_column = c;
  3961. }
  3962. BIM_ACTION(cursor_down, 0,
  3963. "Move the cursor one line down."
  3964. )(void) {
  3965. /* If this isn't already the last line... */
  3966. if (env->line_no < env->line_count) {
  3967. /* Move the cursor down */
  3968. env->line_no += 1;
  3969. /* Try to place the cursor horizontally at the preferred column */
  3970. int _x = 0;
  3971. for (int i = 0; i < env->lines[env->line_no-1]->actual; ++i) {
  3972. char_t * c = &env->lines[env->line_no-1]->text[i];
  3973. _x += c->display_width;
  3974. env->col_no = i+1;
  3975. if (_x > env->preferred_column) {
  3976. break;
  3977. }
  3978. }
  3979. if (env->mode == MODE_INSERT && _x <= env->preferred_column) {
  3980. env->col_no = env->lines[env->line_no-1]->actual + 1;
  3981. }
  3982. /*
  3983. * If the horizontal cursor position exceeds the width the new line,
  3984. * then move the cursor left to the extent of the new line.
  3985. *
  3986. * If we're in insert mode, we can go one cell beyond the end of the line
  3987. */
  3988. if (env->col_no > env->lines[env->line_no-1]->actual + (env->mode == MODE_INSERT)) {
  3989. env->col_no = env->lines[env->line_no-1]->actual + (env->mode == MODE_INSERT);
  3990. if (env->col_no == 0) env->col_no = 1;
  3991. }
  3992. if (env->loading) return;
  3993. /*
  3994. * If the screen was scrolled horizontally, unscroll it;
  3995. * if it will be scrolled on this line as well, that will
  3996. * be handled by place_cursor_actual
  3997. */
  3998. int redraw = 0;
  3999. if (env->coffset != 0) {
  4000. env->coffset = 0;
  4001. redraw = 1;
  4002. }
  4003. /* If we've scrolled past the bottom of the screen, scroll the screen */
  4004. if (env->line_no > env->offset + global_config.term_height - global_config.bottom_size - global_config.tabs_visible - global_config.cursor_padding) {
  4005. env->offset += 1;
  4006. /* Tell terminal to scroll */
  4007. if (global_config.can_scroll && !left_buffer) {
  4008. if (!global_config.can_insert) {
  4009. shift_up(1);
  4010. redraw_tabbar();
  4011. } else {
  4012. delete_lines_at(global_config.tabs_visible ? 2 : 1, 1);
  4013. }
  4014. /* A new line appears on screen at the bottom, draw it */
  4015. int l = global_config.term_height - global_config.bottom_size - global_config.tabs_visible;
  4016. if (env->offset + l < env->line_count + 1) {
  4017. redraw_line(env->offset + l-1);
  4018. } else {
  4019. draw_excess_line(l - 1);
  4020. }
  4021. } else {
  4022. redraw_text();
  4023. }
  4024. redraw_statusbar();
  4025. redraw_commandline();
  4026. place_cursor_actual();
  4027. return;
  4028. } else if (redraw) {
  4029. /* Otherwise, if we need to redraw because of coffset change, do that */
  4030. redraw_text();
  4031. }
  4032. set_history_break();
  4033. /* Update the status bar */
  4034. redraw_statusbar();
  4035. /* Place the terminal cursor again */
  4036. place_cursor_actual();
  4037. }
  4038. }
  4039. BIM_ACTION(cursor_up, 0,
  4040. "Move the cursor up one line."
  4041. )(void) {
  4042. /* If this isn't the first line already */
  4043. if (env->line_no > 1) {
  4044. /* Move the cursor down */
  4045. env->line_no -= 1;
  4046. /* Try to place the cursor horizontally at the preferred column */
  4047. int _x = 0;
  4048. for (int i = 0; i < env->lines[env->line_no-1]->actual; ++i) {
  4049. char_t * c = &env->lines[env->line_no-1]->text[i];
  4050. _x += c->display_width;
  4051. env->col_no = i+1;
  4052. if (_x > env->preferred_column) {
  4053. break;
  4054. }
  4055. }
  4056. if (env->mode == MODE_INSERT && _x <= env->preferred_column) {
  4057. env->col_no = env->lines[env->line_no-1]->actual + 1;
  4058. }
  4059. /*
  4060. * If the horizontal cursor position exceeds the width the new line,
  4061. * then move the cursor left to the extent of the new line.
  4062. *
  4063. * If we're in insert mode, we can go one cell beyond the end of the line
  4064. */
  4065. if (env->col_no > env->lines[env->line_no-1]->actual + (env->mode == MODE_INSERT)) {
  4066. env->col_no = env->lines[env->line_no-1]->actual + (env->mode == MODE_INSERT);
  4067. if (env->col_no == 0) env->col_no = 1;
  4068. }
  4069. if (env->loading) return;
  4070. /*
  4071. * If the screen was scrolled horizontally, unscroll it;
  4072. * if it will be scrolled on this line as well, that will
  4073. * be handled by place_cursor_actual
  4074. */
  4075. int redraw = 0;
  4076. if (env->coffset != 0) {
  4077. env->coffset = 0;
  4078. redraw = 1;
  4079. }
  4080. int e = (env->offset == 0) ? env->offset : env->offset + global_config.cursor_padding;
  4081. if (env->line_no <= e) {
  4082. env->offset -= 1;
  4083. /* Tell terminal to scroll */
  4084. if (global_config.can_scroll && !left_buffer) {
  4085. if (!global_config.can_insert) {
  4086. shift_down(1);
  4087. redraw_tabbar();
  4088. } else {
  4089. insert_lines_at(global_config.tabs_visible ? 2 : 1, 1);
  4090. }
  4091. /*
  4092. * The line at the top of the screen should always be real
  4093. * so we can just call redraw_line here
  4094. */
  4095. redraw_line(env->offset);
  4096. } else {
  4097. redraw_tabbar();
  4098. redraw_text();
  4099. }
  4100. redraw_statusbar();
  4101. redraw_commandline();
  4102. place_cursor_actual();
  4103. return;
  4104. } else if (redraw) {
  4105. /* Otherwise, if we need to redraw because of coffset change, do that */
  4106. redraw_text();
  4107. }
  4108. set_history_break();
  4109. /* Update the status bar */
  4110. redraw_statusbar();
  4111. /* Place the terminal cursor again */
  4112. place_cursor_actual();
  4113. }
  4114. }
  4115. BIM_ACTION(cursor_left, 0,
  4116. "Move the cursor one character to the left."
  4117. )(void) {
  4118. if (env->col_no > 1) {
  4119. env->col_no -= 1;
  4120. /* Update the status bar */
  4121. redraw_statusbar();
  4122. /* Place the terminal cursor again */
  4123. place_cursor_actual();
  4124. }
  4125. set_history_break();
  4126. set_preferred_column();
  4127. }
  4128. BIM_ACTION(cursor_right, 0,
  4129. "Move the cursor one character to the right."
  4130. )(void) {
  4131. /* If this isn't already the rightmost column we can reach on this line in this mode... */
  4132. if (env->col_no < env->lines[env->line_no-1]->actual + !!(env->mode == MODE_INSERT)) {
  4133. env->col_no += 1;
  4134. /* Update the status bar */
  4135. redraw_statusbar();
  4136. /* Place the terminal cursor again */
  4137. place_cursor_actual();
  4138. }
  4139. set_history_break();
  4140. set_preferred_column();
  4141. }
  4142. BIM_ACTION(cursor_home, 0,
  4143. "Move the cursor to the beginning of the line."
  4144. )(void) {
  4145. env->col_no = 1;
  4146. set_history_break();
  4147. set_preferred_column();
  4148. /* Update the status bar */
  4149. redraw_statusbar();
  4150. /* Place the terminal cursor again */
  4151. place_cursor_actual();
  4152. }
  4153. BIM_ACTION(cursor_end, 0,
  4154. "Move the cursor to the end of the line, or past the end in insert mode."
  4155. )(void) {
  4156. env->col_no = env->lines[env->line_no-1]->actual+!!(env->mode == MODE_INSERT);
  4157. set_history_break();
  4158. set_preferred_column();
  4159. /* Update the status bar */
  4160. redraw_statusbar();
  4161. /* Place the terminal cursor again */
  4162. place_cursor_actual();
  4163. }
  4164. BIM_ACTION(leave_insert, 0,
  4165. "Leave insert modes and return to normal mode."
  4166. )(void) {
  4167. if (env->col_no > env->lines[env->line_no-1]->actual) {
  4168. env->col_no = env->lines[env->line_no-1]->actual;
  4169. if (env->col_no == 0) env->col_no = 1;
  4170. set_preferred_column();
  4171. }
  4172. set_history_break();
  4173. env->mode = MODE_NORMAL;
  4174. redraw_commandline();
  4175. }
  4176. /**
  4177. * Helper for handling smart case sensitivity.
  4178. */
  4179. int search_matches(uint32_t a, uint32_t b, int mode) {
  4180. if (mode == 0) {
  4181. return a == b;
  4182. } else if (mode == 1) {
  4183. return tolower(a) == tolower(b);
  4184. }
  4185. return 0;
  4186. }
  4187. int subsearch_matches(line_t * line, int j, uint32_t * needle, int ignorecase, int *len) {
  4188. int k = j;
  4189. uint32_t * match = needle;
  4190. if (*match == '^') {
  4191. if (j != 0) return 0;
  4192. match++;
  4193. }
  4194. while (k < line->actual + 1) {
  4195. if (*match == '\0') {
  4196. if (len) *len = k - j;
  4197. return 1;
  4198. }
  4199. if (*match == '$') {
  4200. if (k != line->actual) return 0;
  4201. match++;
  4202. continue;
  4203. }
  4204. if (*match == '.') {
  4205. if (match[1] == '*') {
  4206. int greedy = !(match[2] == '?');
  4207. /* Short-circuit chained .*'s */
  4208. if (match[greedy ? 2 : 3] == '.' && match[greedy ? 3 : 4] == '*') {
  4209. int _len;
  4210. if (subsearch_matches(line, k, &match[greedy ? 2 : 3], ignorecase, &_len)) {
  4211. if (len) *len = _len + k - j;
  4212. return 1;
  4213. }
  4214. return 0;
  4215. }
  4216. int _j = greedy ? line->actual : k;
  4217. int _break = -1;
  4218. int _len = -1;
  4219. if (!match[greedy ? 2 : 3]) {
  4220. _len = greedy ? (line->actual - _j) : 0;
  4221. _break = _j;
  4222. } else {
  4223. while (_j < line->actual + 1 && _j >= k) {
  4224. int len;
  4225. if (subsearch_matches(line, _j, &match[greedy ? 2 : 3], ignorecase, &len)) {
  4226. _break = _j;
  4227. _len = len;
  4228. break;
  4229. }
  4230. _j += (greedy ? -1 : 1);
  4231. }
  4232. }
  4233. if (_break != -1) {
  4234. if (len) *len = (_break - j) + _len;
  4235. return 1;
  4236. }
  4237. return 0;
  4238. } else {
  4239. if (k >= line->actual) return 0;
  4240. match++;
  4241. k++;
  4242. continue;
  4243. }
  4244. }
  4245. if (*match == '\\' && (match[1] == '$' || match[1] == '^' || match[1] == '/' || match[1] == '\\' || match[1] == '.')) {
  4246. match++;
  4247. } else if (*match == '\\' && match[1] == 't') {
  4248. if (line->text[k].codepoint != '\t') break;
  4249. match += 2;
  4250. k++;
  4251. continue;
  4252. }
  4253. if (k == line->actual) break;
  4254. if (!search_matches(*match, line->text[k].codepoint, ignorecase)) break;
  4255. match++;
  4256. k++;
  4257. }
  4258. return 0;
  4259. }
  4260. /**
  4261. * Replace text on a given line with other text.
  4262. */
  4263. void perform_replacement(int line_no, uint32_t * needle, uint32_t * replacement, int col, int ignorecase, int *out_col) {
  4264. line_t * line = env->lines[line_no-1];
  4265. int j = col;
  4266. while (j < line->actual + 1) {
  4267. int match_len;
  4268. if (subsearch_matches(line,j,needle,ignorecase,&match_len)) {
  4269. /* Perform replacement */
  4270. for (int i = 0; i < match_len; ++i) {
  4271. line_delete(line, j+1, line_no-1);
  4272. }
  4273. int t = 0;
  4274. for (uint32_t * r = replacement; *r; ++r) {
  4275. char_t _c;
  4276. _c.codepoint = *r;
  4277. _c.flags = 0;
  4278. _c.display_width = codepoint_width(*r);
  4279. line_t * nline = line_insert(line, _c, j + t, line_no -1);
  4280. if (line != nline) {
  4281. env->lines[line_no-1] = nline;
  4282. line = nline;
  4283. }
  4284. t++;
  4285. }
  4286. *out_col = j + t;
  4287. set_modified();
  4288. return;
  4289. }
  4290. j++;
  4291. }
  4292. *out_col = -1;
  4293. }
  4294. #define COMMAND_HISTORY_MAX 255
  4295. unsigned char * command_history[COMMAND_HISTORY_MAX] = {NULL};
  4296. /**
  4297. * Add a command to the history. If that command was
  4298. * already in history, it is moved to the front of the list;
  4299. * otherwise, the whole list is shifted backwards and
  4300. * overflow is freed up.
  4301. */
  4302. void insert_command_history(char * cmd) {
  4303. /* See if this is already in the history. */
  4304. size_t amount_to_shift = COMMAND_HISTORY_MAX - 1;
  4305. for (int i = 0; i < COMMAND_HISTORY_MAX && command_history[i]; ++i) {
  4306. if (!strcmp((char*)command_history[i], cmd)) {
  4307. free(command_history[i]);
  4308. amount_to_shift = i;
  4309. break;
  4310. }
  4311. }
  4312. /* Remove last entry that will roll off the stack */
  4313. if (amount_to_shift == COMMAND_HISTORY_MAX - 1) {
  4314. if (command_history[COMMAND_HISTORY_MAX-1]) free(command_history[COMMAND_HISTORY_MAX-1]);
  4315. }
  4316. /* Roll the history */
  4317. memmove(&command_history[1], &command_history[0], sizeof(char *) * (amount_to_shift));
  4318. command_history[0] = (unsigned char*)strdup(cmd);
  4319. }
  4320. static uint32_t term_colors[] = {
  4321. 0x000000, 0xcc0000, 0x3e9a06, 0xc4a000, 0x3465a4, 0x75507b, 0x06989a, 0xeeeeec, 0x555753, 0xef2929, 0x8ae234, 0xfce94f, 0x729fcf, 0xad7fa8, 0x34e2e2,
  4322. 0xFFFFFF, 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f,
  4323. 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7,
  4324. 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f,
  4325. 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7,
  4326. 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f,
  4327. 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7,
  4328. 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
  4329. 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7,
  4330. 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f,
  4331. 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7,
  4332. 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f,
  4333. 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7,
  4334. 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f,
  4335. 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7,
  4336. 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
  4337. 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4,
  4338. 0xeeeeee,
  4339. };
  4340. /**
  4341. * Convert a color setting from terminal format
  4342. * to a hexadecimal color code and add it to the current
  4343. * buffer. This is used for HTML conversion, but could
  4344. * possibly be used for other purposes.
  4345. */
  4346. static void html_convert_color(const char * color_string) {
  4347. char tmp[100];
  4348. if (!strncmp(color_string,"2;",2)) {
  4349. /* 24-bit color */
  4350. int red, green, blue;
  4351. sscanf(color_string+2,"%d;%d;%d",&red,&green,&blue);
  4352. sprintf(tmp, "#%02x%02x%02x;", red,green,blue);
  4353. } else if (!strncmp(color_string,"5;",2)) {
  4354. /* 256 colors; needs lookup table */
  4355. int index;
  4356. sscanf(color_string+2,"%d",&index);
  4357. sprintf(tmp,"#%06x;", (unsigned int)term_colors[(index >= 0 && index <= 255) ? index : 0]);
  4358. } else {
  4359. /* 16 colors; needs lookup table */
  4360. int index;
  4361. uint32_t color;
  4362. sscanf(color_string+1,"%d",&index);
  4363. if (index >= 10) {
  4364. index -= 10;
  4365. color = term_colors[index+8];
  4366. } else if (index == 9) {
  4367. color = term_colors[0];
  4368. } else {
  4369. color = term_colors[index];
  4370. }
  4371. sprintf(tmp,"#%06x;", (unsigned int)color);
  4372. }
  4373. add_string(tmp);
  4374. char * italic = strstr(color_string,";3");
  4375. if (italic && italic[2] == '\0') {
  4376. add_string(" font-style: oblique;");
  4377. }
  4378. char * bold = strstr(color_string,";1");
  4379. if (bold && bold[2] == '\0') {
  4380. add_string(" font-weight: bold;");
  4381. }
  4382. char * underline = strstr(color_string,";4");
  4383. if (underline && underline[2] == '\0') {
  4384. add_string(" font-decoration: underline;");
  4385. }
  4386. }
  4387. int convert_to_html(void) {
  4388. buffer_t * old = env;
  4389. env = buffer_new();
  4390. setup_buffer(env);
  4391. env->loading = 1;
  4392. add_string("<!doctype html>\n");
  4393. add_string("<html>\n");
  4394. add_string(" <head>\n");
  4395. add_string(" <meta charset=\"UTF-8\">\n");
  4396. if (old->file_name) {
  4397. add_string(" <title>");
  4398. add_string(file_basename(old->file_name));
  4399. add_string("</title>\n");
  4400. }
  4401. add_string(" <style>\n");
  4402. add_string(" body {\n");
  4403. add_string(" margin: 0;\n");
  4404. add_string(" -webkit-text-size-adjust: none;\n");
  4405. add_string(" counter-reset: line-no;\n");
  4406. add_string(" background-color: ");
  4407. /* Convert color */
  4408. html_convert_color(COLOR_BG);
  4409. add_string("\n");
  4410. add_string(" }\n");
  4411. for (int i = 0; i < 15; ++i) {
  4412. /* For each of the relevant flags... */
  4413. char tmp[20];
  4414. sprintf(tmp," .s%d { color: ", i);
  4415. add_string(tmp);
  4416. /* These are special */
  4417. if (i == FLAG_NOTICE) {
  4418. html_convert_color(COLOR_SEARCH_FG);
  4419. add_string(" background-color: ");
  4420. html_convert_color(COLOR_SEARCH_BG);
  4421. } else if (i == FLAG_ERROR) {
  4422. html_convert_color(COLOR_ERROR_FG);
  4423. add_string(" background-color: ");
  4424. html_convert_color(COLOR_ERROR_BG);
  4425. } else {
  4426. html_convert_color(flag_to_color(i));
  4427. }
  4428. add_string("}\n");
  4429. }
  4430. add_string(" pre {\n");
  4431. add_string(" margin: 0;\n");
  4432. add_string(" white-space: pre-wrap;\n");
  4433. add_string(" font-family: \"DejaVu Sans Mono\", Courier, monospace;\n");
  4434. add_string(" font-size: 10pt;\n");
  4435. add_string(" }\n");
  4436. add_string(" pre>span {\n");
  4437. add_string(" display: inline-block;\n");
  4438. add_string(" width: 100%;\n");
  4439. add_string(" }\n");
  4440. add_string(" pre>span>a::before {\n");
  4441. add_string(" counter-increment: line-no;\n");
  4442. add_string(" content: counter(line-no);\n");
  4443. add_string(" padding-right: 1em;\n");
  4444. add_string(" width: 3em;\n");
  4445. add_string(" display: inline-block;\n");
  4446. add_string(" text-align: right;\n");
  4447. add_string(" background-color: ");
  4448. html_convert_color(COLOR_NUMBER_BG);
  4449. add_string("\n");
  4450. add_string(" color: ");
  4451. html_convert_color(COLOR_NUMBER_FG);
  4452. add_string("\n");
  4453. add_string(" }\n");
  4454. add_string(" pre>span:target {\n");
  4455. add_string(" background-color: ");
  4456. html_convert_color(COLOR_ALT_BG);
  4457. add_string("\n");
  4458. add_string(" }\n");
  4459. add_string(" pre>span:target>a::before {\n");
  4460. add_string(" background-color: ");
  4461. html_convert_color(COLOR_NUMBER_FG);
  4462. add_string("\n");
  4463. add_string(" color: ");
  4464. html_convert_color(COLOR_NUMBER_BG);
  4465. add_string("\n");
  4466. add_string(" }\n");
  4467. for (int i = 1; i <= env->tabstop; ++i) {
  4468. char tmp[10];
  4469. sprintf(tmp, ".tab%d", i);
  4470. add_string(" ");
  4471. add_string(tmp);
  4472. add_string(">span {\n");
  4473. add_string(" display: inline-block;\n");
  4474. add_string(" overflow: hidden;\n");
  4475. add_string(" width: 0;\n");
  4476. add_string(" height: 0;\n");
  4477. add_string(" }\n");
  4478. add_string(" ");
  4479. add_string(tmp);
  4480. add_string("::after {\n");
  4481. add_string(" content: '»");
  4482. for (int j = 1; j < i; ++j) {
  4483. add_string("·");
  4484. }
  4485. add_string("';\n");
  4486. add_string(" background-color: ");
  4487. html_convert_color(COLOR_ALT_BG);
  4488. add_string("\n");
  4489. add_string(" color: ");
  4490. html_convert_color(COLOR_ALT_FG);
  4491. add_string("\n");
  4492. add_string(" }\n");
  4493. }
  4494. add_string(" .space {\n");
  4495. add_string(" border-left: 1px solid ");
  4496. html_convert_color(COLOR_ALT_FG);
  4497. add_string("\n");
  4498. add_string(" margin-left: -1px;\n");
  4499. add_string(" }\n");
  4500. add_string(" </style>\n");
  4501. add_string(" </head>\n");
  4502. add_string(" <body><pre>\n");
  4503. for (int i = 0; i < old->line_count; ++i) {
  4504. char tmp[100];
  4505. sprintf(tmp, "<span id=\"L%d\"><a href=\"#L%d\"></a>", i+1, i+1);
  4506. add_string(tmp);
  4507. int last_flag = -1;
  4508. int opened = 0;
  4509. int all_spaces = 1;
  4510. for (int j = 0; j < old->lines[i]->actual; ++j) {
  4511. char_t c = old->lines[i]->text[j];
  4512. if (c.codepoint != ' ') all_spaces = 0;
  4513. if (last_flag == -1 || last_flag != (c.flags & 0x1F)) {
  4514. if (opened) add_string("</span>");
  4515. opened = 1;
  4516. char tmp[100];
  4517. sprintf(tmp, "<span class=\"s%d\">", c.flags & 0x1F);
  4518. add_string(tmp);
  4519. last_flag = (c.flags & 0x1F);
  4520. }
  4521. if (c.codepoint == '<') {
  4522. add_string("&lt;");
  4523. } else if (c.codepoint == '>') {
  4524. add_string("&gt;");
  4525. } else if (c.codepoint == '&') {
  4526. add_string("&amp;");
  4527. } else if (c.codepoint == '\t') {
  4528. char tmp[100];
  4529. sprintf(tmp, "<span class=\"tab%d\"><span> </span></span>",c.display_width);
  4530. add_string(tmp);
  4531. } else if (j > 0 && c.codepoint == ' ' && all_spaces && !(j % old->tabstop)) {
  4532. add_string("<span class=\"space\"> </span>");
  4533. } else {
  4534. char tmp[7] = {0}; /* Max six bytes, use 7 to ensure last is always nil */
  4535. to_eight(c.codepoint, tmp);
  4536. add_string(tmp);
  4537. }
  4538. }
  4539. if (opened) {
  4540. add_string("</span>");
  4541. } else {
  4542. add_string("<wbr>");
  4543. }
  4544. add_string("</span>\n");
  4545. }
  4546. add_string("</pre></body>\n");
  4547. add_string("</html>\n");
  4548. env->loading = 0;
  4549. env->modified = 1;
  4550. if (old->file_name) {
  4551. char * base = file_basename(old->file_name);
  4552. char * tmp = malloc(strlen(base) + 5);
  4553. *tmp = '\0';
  4554. strcat(tmp, base);
  4555. strcat(tmp, ".htm");
  4556. env->file_name = tmp;
  4557. }
  4558. for (int i = 0; i < env->line_count; ++i) {
  4559. recalculate_tabs(env->lines[i]);
  4560. }
  4561. env->syntax = match_syntax(".htm");
  4562. for (int i = 0; i < env->line_count; ++i) {
  4563. recalculate_syntax(env->lines[i],i);
  4564. }
  4565. return 0;
  4566. }
  4567. /**
  4568. * Based on vim's :TOhtml
  4569. * Convert syntax-highlighted buffer contents to HTML.
  4570. */
  4571. BIM_COMMAND(tohtml,"tohtml","Convert the document to an HTML representation with syntax highlighting.") {
  4572. convert_to_html();
  4573. redraw_all();
  4574. return 0;
  4575. }
  4576. BIM_ALIAS("TOhtml",tohtml,tohtml)
  4577. int _prefix_command_run_script(char * cmd) {
  4578. if (env->mode == MODE_LINE_SELECTION) {
  4579. int range_top, range_bot;
  4580. range_top = env->start_line < env->line_no ? env->start_line : env->line_no;
  4581. range_bot = env->start_line < env->line_no ? env->line_no : env->start_line;
  4582. int in[2];
  4583. pipe(in);
  4584. int out[2];
  4585. pipe(out);
  4586. int child = fork();
  4587. /* Open child process and set up pipes */
  4588. if (child == 0) {
  4589. FILE * dev_null = fopen("/dev/null","w"); /* for stderr */
  4590. close(out[0]);
  4591. close(in[1]);
  4592. dup2(out[1], STDOUT_FILENO);
  4593. dup2(in[0], STDIN_FILENO);
  4594. dup2(fileno(dev_null), STDERR_FILENO);
  4595. if (*cmd == '!') {
  4596. system(&cmd[1]); /* Yes we can just do this */
  4597. } else {
  4598. char * const args[] = {"python3","-c",&cmd[1],NULL};
  4599. execvp("python3",args);
  4600. }
  4601. exit(1);
  4602. } else if (child < 0) {
  4603. render_error("Failed to fork");
  4604. return 1;
  4605. }
  4606. close(out[1]);
  4607. close(in[0]);
  4608. /* Write lines to child process */
  4609. FILE * f = fdopen(in[1],"w");
  4610. for (int i = range_top; i <= range_bot; ++i) {
  4611. line_t * line = env->lines[i-1];
  4612. for (int j = 0; j < line->actual; j++) {
  4613. char_t c = line->text[j];
  4614. if (c.codepoint == 0) {
  4615. char buf[1] = {0};
  4616. fwrite(buf, 1, 1, f);
  4617. } else {
  4618. char tmp[8] = {0};
  4619. int i = to_eight(c.codepoint, tmp);
  4620. fwrite(tmp, i, 1, f);
  4621. }
  4622. }
  4623. fputc('\n', f);
  4624. }
  4625. fclose(f);
  4626. close(in[1]);
  4627. /* Read results from child process into a new buffer */
  4628. FILE * result = fdopen(out[0],"r");
  4629. buffer_t * old = env;
  4630. env = buffer_new();
  4631. setup_buffer(env);
  4632. env->loading = 1;
  4633. uint8_t buf[BLOCK_SIZE];
  4634. state = 0;
  4635. while (!feof(result) && !ferror(result)) {
  4636. size_t r = fread(buf, 1, BLOCK_SIZE, result);
  4637. add_buffer(buf, r);
  4638. }
  4639. if (env->line_no && env->lines[env->line_no-1] && env->lines[env->line_no-1]->actual == 0) {
  4640. env->lines = remove_line(env->lines, env->line_no-1);
  4641. }
  4642. fclose(result);
  4643. env->loading = 0;
  4644. /* Return to the original buffer and replace the selected lines with the output */
  4645. buffer_t * new = env;
  4646. env = old;
  4647. for (int i = range_top; i <= range_bot; ++i) {
  4648. /* Remove the existing lines */
  4649. env->lines = remove_line(env->lines, range_top-1);
  4650. }
  4651. for (int i = 0; i < new->line_count; ++i) {
  4652. /* Add the new lines */
  4653. env->lines = add_line(env->lines, range_top + i - 1);
  4654. replace_line(env->lines, range_top + i - 1, new->lines[i]);
  4655. recalculate_tabs(env->lines[range_top+i-1]);
  4656. }
  4657. env->modified = 1;
  4658. /* Close the temporary buffer */
  4659. buffer_close(new);
  4660. } else {
  4661. /* Reset and draw some line feeds */
  4662. reset();
  4663. printf("\n\n");
  4664. /* Set buffered for shell application */
  4665. set_buffered();
  4666. /* Call the shell and wait for completion */
  4667. if (*cmd == '!') {
  4668. system(&cmd[1]);
  4669. } else {
  4670. setenv("PYCMD",&cmd[1],1);
  4671. system("python3 -c \"$PYCMD\"");
  4672. }
  4673. /* Return to the editor, wait for user to press enter. */
  4674. set_unbuffered();
  4675. printf("\n\nPress ENTER to continue.");
  4676. int c;
  4677. while ((c = bim_getch(), c != ENTER_KEY && c != LINE_FEED));
  4678. /* Redraw the screen */
  4679. redraw_all();
  4680. }
  4681. /* Done processing command */
  4682. return 0;
  4683. }
  4684. BIM_PREFIX_COMMAND(bang,"!","Executes shell commands.") {
  4685. (void)argc, (void)argv;
  4686. return _prefix_command_run_script(cmd);
  4687. }
  4688. BIM_PREFIX_COMMAND(tick,"`","Executes Python commands.") {
  4689. (void)argc, (void)argv;
  4690. return _prefix_command_run_script(cmd);
  4691. }
  4692. int replace_text(int range_top, int range_bot, char divider, char * needle) {
  4693. char * c = needle;
  4694. char * replacement = NULL;
  4695. char * options = "";
  4696. while (*c) {
  4697. if (*c == divider) {
  4698. *c = '\0';
  4699. replacement = c + 1;
  4700. break;
  4701. }
  4702. c++;
  4703. }
  4704. if (!replacement) {
  4705. render_error("nothing to replace with");
  4706. return 1;
  4707. }
  4708. c = replacement;
  4709. while (*c) {
  4710. if (*c == divider) {
  4711. *c = '\0';
  4712. options = c + 1;
  4713. break;
  4714. }
  4715. c++;
  4716. }
  4717. int global = 0;
  4718. int case_insensitive = 0;
  4719. /* Parse options */
  4720. while (*options) {
  4721. switch (*options) {
  4722. case 'g':
  4723. global = 1;
  4724. break;
  4725. case 'i':
  4726. case_insensitive = 1;
  4727. break;
  4728. }
  4729. options++;
  4730. }
  4731. uint32_t * needle_c = malloc(sizeof(uint32_t) * (strlen(needle) + 1));
  4732. uint32_t * replacement_c = malloc(sizeof(uint32_t) * (strlen(replacement) + 1));
  4733. {
  4734. int i = 0;
  4735. uint32_t c, state = 0;
  4736. for (char * cin = needle; *cin; cin++) {
  4737. if (!decode(&state, &c, *cin)) {
  4738. needle_c[i] = c;
  4739. i++;
  4740. } else if (state == UTF8_REJECT) {
  4741. state = 0;
  4742. }
  4743. }
  4744. needle_c[i] = 0;
  4745. i = 0;
  4746. c = 0;
  4747. state = 0;
  4748. for (char * cin = replacement; *cin; cin++) {
  4749. if (!decode(&state, &c, *cin)) {
  4750. replacement_c[i] = c;
  4751. i++;
  4752. } else if (state == UTF8_REJECT) {
  4753. state = 0;
  4754. }
  4755. }
  4756. replacement_c[i] = 0;
  4757. }
  4758. int replacements = 0;
  4759. for (int line = range_top; line <= range_bot; ++line) {
  4760. int col = 0;
  4761. while (col != -1) {
  4762. perform_replacement(line, needle_c, replacement_c, col, case_insensitive, &col);
  4763. if (col != -1) replacements++;
  4764. if (!global) break;
  4765. }
  4766. }
  4767. free(needle_c);
  4768. free(replacement_c);
  4769. if (replacements) {
  4770. render_status_message("replaced %d instance%s of %s", replacements, replacements == 1 ? "" : "s", needle);
  4771. set_history_break();
  4772. redraw_text();
  4773. } else {
  4774. render_error("Pattern not found: %s", needle);
  4775. }
  4776. return 0;
  4777. }
  4778. BIM_PREFIX_COMMAND(repsome,"s","Perform a replacement over selected lines") {
  4779. int range_top, range_bot;
  4780. if (env->mode == MODE_LINE_SELECTION) {
  4781. range_top = env->start_line < env->line_no ? env->start_line : env->line_no;
  4782. range_bot = env->start_line < env->line_no ? env->line_no : env->start_line;
  4783. } else {
  4784. range_top = env->line_no;
  4785. range_bot = env->line_no;
  4786. }
  4787. return replace_text(range_top, range_bot, cmd[1], &cmd[2]);
  4788. }
  4789. BIM_PREFIX_COMMAND(repall,"%s","Perform a replacement over the entire file.") {
  4790. return replace_text(1, env->line_count, cmd[2], &cmd[3]);
  4791. }
  4792. BIM_COMMAND(e,"e","Open a file") {
  4793. if (argc > 1) {
  4794. /* This actually opens a new tab */
  4795. open_file(argv[1]);
  4796. update_title();
  4797. } else {
  4798. if (env->modified) {
  4799. render_error("File is modified, can not reload.");
  4800. return 1;
  4801. }
  4802. buffer_t * old_env = env;
  4803. open_file(env->file_name);
  4804. buffer_t * new_env = env;
  4805. env = old_env;
  4806. #define SWAP(T,a,b) do { T x = a; a = b; b = x; } while (0)
  4807. SWAP(line_t **, env->lines, new_env->lines);
  4808. SWAP(int, env->line_count, new_env->line_count);
  4809. SWAP(int, env->line_avail, new_env->line_avail);
  4810. SWAP(history_t *, env->history, new_env->history);
  4811. buffer_close(new_env); /* Should probably also free, this needs editing. */
  4812. redraw_all();
  4813. }
  4814. return 0;
  4815. }
  4816. BIM_COMMAND(tabnew,"tabnew","Open a new tab") {
  4817. if (argc > 1) {
  4818. open_file(argv[1]);
  4819. update_title();
  4820. } else {
  4821. env = buffer_new();
  4822. setup_buffer(env);
  4823. redraw_all();
  4824. update_title();
  4825. }
  4826. return 0;
  4827. }
  4828. BIM_COMMAND(w,"w","Write a file") {
  4829. /* w: write file */
  4830. if (argc > 1) {
  4831. write_file(argv[1]);
  4832. } else {
  4833. write_file(env->file_name);
  4834. }
  4835. return 0;
  4836. }
  4837. BIM_COMMAND(wq,"wq","Write and close buffer") {
  4838. write_file(env->file_name);
  4839. close_buffer();
  4840. return 0;
  4841. }
  4842. BIM_COMMAND(history,"history","Display command history") {
  4843. render_commandline_message(""); /* To clear command line */
  4844. for (int i = COMMAND_HISTORY_MAX; i > 1; --i) {
  4845. if (command_history[i-1]) render_commandline_message("%d:%s\n", i-1, command_history[i-1]);
  4846. }
  4847. render_commandline_message("\n");
  4848. redraw_tabbar();
  4849. redraw_commandline();
  4850. pause_for_key();
  4851. return 0;
  4852. }
  4853. BIM_COMMAND(q,"q","Close buffer") {
  4854. if (left_buffer && left_buffer == right_buffer) {
  4855. unsplit();
  4856. return 0;
  4857. }
  4858. if (env->modified) {
  4859. render_error("No write since last change. Use :q! to force exit.");
  4860. } else {
  4861. close_buffer();
  4862. }
  4863. update_title();
  4864. return 0;
  4865. }
  4866. BIM_COMMAND(qbang,"q!","Force close buffer") {
  4867. close_buffer();
  4868. update_title();
  4869. return 0;
  4870. }
  4871. BIM_COMMAND(qa,"qa","Try to close all buffers") {
  4872. try_quit();
  4873. return 0;
  4874. }
  4875. BIM_ALIAS("qall",qall,qa)
  4876. BIM_COMMAND(qabang,"qa!","Force exit") {
  4877. /* Forcefully exit editor */
  4878. while (buffers_len) {
  4879. buffer_close(buffers[0]);
  4880. }
  4881. quit(NULL);
  4882. return 1; /* doesn't return */
  4883. }
  4884. BIM_COMMAND(tabp,"tabp","Previous tab") {
  4885. previous_tab();
  4886. update_title();
  4887. return 0;
  4888. }
  4889. BIM_COMMAND(tabn,"tabn","Next tab") {
  4890. next_tab();
  4891. update_title();
  4892. return 0;
  4893. }
  4894. BIM_COMMAND(tabindicator,"tabindicator","Set the tab indicator") {
  4895. if (argc < 2) {
  4896. render_status_message("tabindicator=%s", global_config.tab_indicator);
  4897. return 0;
  4898. }
  4899. if (display_width_of_string(argv[1]) != 1) {
  4900. render_error("Can't set '%s' as indicator, must be one cell wide.", argv[1]);
  4901. return 1;
  4902. }
  4903. if (global_config.tab_indicator) free(global_config.tab_indicator);
  4904. global_config.tab_indicator = strdup(argv[1]);
  4905. return 0;
  4906. }
  4907. BIM_COMMAND(spaceindicator,"spaceindicator","Set the space indicator") {
  4908. if (argc < 2) {
  4909. render_status_message("spaceindicator=%s", global_config.space_indicator);
  4910. return 0;
  4911. }
  4912. if (display_width_of_string(argv[1]) != 1) {
  4913. render_error("Can't set '%s' as indicator, must be one cell wide.", argv[1]);
  4914. return 1;
  4915. }
  4916. if (global_config.space_indicator) free(global_config.space_indicator);
  4917. global_config.space_indicator = strdup(argv[1]);
  4918. return 0;
  4919. }
  4920. BIM_COMMAND(global_sgr,"global.sgr_mouse","Enable SGR mouse escapes") {
  4921. if (argc < 2) {
  4922. render_status_message("global.sgr_mouse=%d", global_config.use_sgr_mouse);
  4923. } else {
  4924. if (global_config.has_terminal) mouse_disable();
  4925. global_config.use_sgr_mouse = !!atoi(argv[1]);
  4926. if (global_config.has_terminal) mouse_enable();
  4927. }
  4928. return 0;
  4929. }
  4930. BIM_COMMAND(global_git,"global.git","Show or change the default status of git integration") {
  4931. if (argc < 2) {
  4932. render_status_message("global.git=%d", global_config.check_git);
  4933. } else {
  4934. global_config.check_git = !!atoi(argv[1]);
  4935. }
  4936. return 0;
  4937. }
  4938. BIM_COMMAND(git,"git","Show or change status of git integration") {
  4939. if (!env) {
  4940. render_error("requires environment (did you mean global.git?)");
  4941. return 1;
  4942. }
  4943. if (argc < 2) {
  4944. render_status_message("git=%d", env->checkgitstatusonwrite);
  4945. } else {
  4946. env->checkgitstatusonwrite = !!atoi(argv[1]);
  4947. if (env->checkgitstatusonwrite && !env->modified && env->file_name) {
  4948. git_examine(env->file_name);
  4949. redraw_text();
  4950. }
  4951. }
  4952. return 0;
  4953. }
  4954. BIM_COMMAND(colorgutter,"colorgutter","Show or change status of gutter colorization for unsaved modifications") {
  4955. if (argc < 2) {
  4956. render_status_message("colorgutter=%d", global_config.color_gutter);
  4957. } else {
  4958. global_config.color_gutter = !!atoi(argv[1]);
  4959. redraw_text();
  4960. }
  4961. return 0;
  4962. }
  4963. BIM_COMMAND(indent,"indent","Enable smart indentation") {
  4964. env->indent = 1;
  4965. redraw_statusbar();
  4966. return 0;
  4967. }
  4968. BIM_COMMAND(noindent,"noindent","Disable smrat indentation") {
  4969. env->indent = 0;
  4970. redraw_statusbar();
  4971. return 0;
  4972. }
  4973. /* TODO: global.maxcolumn */
  4974. BIM_COMMAND(maxcolumn,"maxcolumn","Highlight past the given column to indicate maximum desired line length") {
  4975. if (argc < 2) {
  4976. render_status_message("maxcolumn=%d",env->maxcolumn);
  4977. return 0;
  4978. }
  4979. env->maxcolumn = atoi(argv[1]);
  4980. redraw_text();
  4981. return 0;
  4982. }
  4983. BIM_COMMAND(cursorcolumn,"cursorcolumn","Show the visual column offset of the cursor.") {
  4984. render_status_message("cursorcolumn=%d", env->preferred_column);
  4985. return 0;
  4986. }
  4987. BIM_COMMAND(noh,"noh","Clear search term") {
  4988. if (global_config.search) {
  4989. free(global_config.search);
  4990. global_config.search = NULL;
  4991. for (int i = 0; i < env->line_count; ++i) {
  4992. recalculate_syntax(env->lines[i],i);
  4993. }
  4994. redraw_text();
  4995. }
  4996. return 0;
  4997. }
  4998. BIM_COMMAND(help,"help","Show help text.") {
  4999. if (argc < 2) {
  5000. render_commandline_message(""); /* To clear command line */
  5001. render_commandline_message("\n");
  5002. render_commandline_message(" \033[1mbim - a text editor \033[22m\n");
  5003. render_commandline_message("\n");
  5004. render_commandline_message(" Available commands:\n");
  5005. render_commandline_message(" Quit with \033[3m:q\033[23m, \033[3m:qa\033[23m, \033[3m:q!\033[23m, \033[3m:qa!\033[23m\n");
  5006. render_commandline_message(" Write out with \033[3m:w \033[4mfile\033[24;23m\n");
  5007. render_commandline_message(" Set syntax with \033[3m:syntax \033[4mlanguage\033[24;23m\n");
  5008. render_commandline_message(" Open a new tab with \033[3m:e \033[4mpath/to/file\033[24;23m\n");
  5009. render_commandline_message(" \033[3m:tabn\033[23m and \033[3m:tabp\033[23m can be used to switch tabs\n");
  5010. render_commandline_message(" Set the color scheme with \033[3m:theme \033[4mtheme\033[24;23m\n");
  5011. render_commandline_message(" Set the behavior of the tab key with \033[3m:tabs\033[23m or \033[3m:spaces\033[23m\n");
  5012. render_commandline_message(" Set tabstop with \033[3m:tabstop \033[4mwidth\033[24;23m\n");
  5013. render_commandline_message("\n");
  5014. render_commandline_message(" Bim %s%s\n", BIM_VERSION, BIM_BUILD_DATE);
  5015. render_commandline_message(" %s\n", BIM_COPYRIGHT);
  5016. render_commandline_message("\n");
  5017. } else {
  5018. int found = 0;
  5019. for (struct command_def * c = regular_commands; !found && regular_commands && c->name; ++c) {
  5020. if (!strcmp(c->name, argv[1])) {
  5021. render_commandline_message(""); /* To clear command line */
  5022. render_commandline_message("Help description for `%s`:\n", c->name);
  5023. render_commandline_message(" %s\n", c->description);
  5024. found = 1;
  5025. break;
  5026. }
  5027. }
  5028. for (struct command_def * c = prefix_commands; !found && prefix_commands && c->name; ++c) {
  5029. if (!strcmp(c->name, argv[1])) {
  5030. render_commandline_message(""); /* To clear command line */
  5031. render_commandline_message("Help description for `%s`:\n", c->name);
  5032. render_commandline_message(" %s\n", c->description);
  5033. found = 1;
  5034. break;
  5035. }
  5036. }
  5037. if (!found) {
  5038. render_error("Unknown command: %s", argv[1]);
  5039. return 1;
  5040. }
  5041. }
  5042. /* Redrawing the tabbar makes it look like we just shifted the whole view up */
  5043. redraw_tabbar();
  5044. redraw_commandline();
  5045. /* Wait for a character so we can redraw the screen before continuing */
  5046. pause_for_key();
  5047. return 0;
  5048. }
  5049. BIM_COMMAND(version,"version","Show version information.") {
  5050. render_status_message("Bim %s%s", BIM_VERSION, BIM_BUILD_DATE);
  5051. return 0;
  5052. }
  5053. void load_colorscheme_script(const char * name) {
  5054. static char name_copy[512];
  5055. char tmp[1024];
  5056. snprintf(tmp, 1023, "theme:%s", name);
  5057. if (!run_function(tmp)) {
  5058. sprintf(name_copy, "%s", name);
  5059. current_theme = name_copy;
  5060. }
  5061. }
  5062. BIM_COMMAND(theme,"theme","Set color theme") {
  5063. if (argc < 2) {
  5064. render_status_message("theme=%s", current_theme);
  5065. } else {
  5066. for (struct theme_def * d = themes; themes && d->name; ++d) {
  5067. if (!strcmp(argv[1], d->name)) {
  5068. d->load(d->name);
  5069. redraw_all();
  5070. return 0;
  5071. }
  5072. }
  5073. }
  5074. return 0;
  5075. }
  5076. BIM_ALIAS("colorscheme",colorscheme,theme)
  5077. BIM_COMMAND(splitpercent,"splitpercent","Display or change view split") {
  5078. if (argc < 2) {
  5079. render_status_message("splitpercent=%d", global_config.split_percent);
  5080. return 0;
  5081. } else {
  5082. global_config.split_percent = atoi(argv[1]);
  5083. if (left_buffer) {
  5084. update_split_size();
  5085. redraw_all();
  5086. }
  5087. }
  5088. return 0;
  5089. }
  5090. BIM_COMMAND(split,"split","Split the current view.") {
  5091. buffer_t * original = env;
  5092. if (argc > 1) {
  5093. int is_not_number = 0;
  5094. for (char * c = argv[1]; *c; ++c) is_not_number |= !isdigit(*c);
  5095. if (is_not_number) {
  5096. /* Open a file for the new split */
  5097. open_file(argv[1]);
  5098. right_buffer = buffers[buffers_len-1];
  5099. } else {
  5100. /* Use an existing buffer for the new split */
  5101. int other = atoi(argv[1]);
  5102. if (other >= buffers_len || other < 0) {
  5103. render_error("Invalid buffer number: %d", other);
  5104. return 1;
  5105. }
  5106. right_buffer = buffers[other];
  5107. }
  5108. } else {
  5109. /* Use the current buffer for the new split */
  5110. right_buffer = original;
  5111. }
  5112. left_buffer = original;
  5113. update_split_size();
  5114. redraw_all();
  5115. return 0;
  5116. }
  5117. BIM_COMMAND(unsplit,"unsplit","Show only one buffer on screen") {
  5118. unsplit();
  5119. return 0;
  5120. }
  5121. BIM_COMMAND(horizontalscrolling,"horizontalscrolling","Set the horizontal scrolling mode") {
  5122. if (argc < 2) {
  5123. render_status_message("horizontalscrolling=%d", global_config.horizontal_shift_scrolling);
  5124. return 0;
  5125. } else {
  5126. global_config.horizontal_shift_scrolling = !!atoi(argv[1]);
  5127. redraw_all();
  5128. }
  5129. return 0;
  5130. }
  5131. BIM_COMMAND(syntax,"syntax","Show or set the active syntax highlighter") {
  5132. if (argc < 2) {
  5133. render_status_message("syntax=%s", env->syntax ? env->syntax->name : "none");
  5134. } else {
  5135. set_syntax_by_name(argv[1]);
  5136. }
  5137. return 0;
  5138. }
  5139. BIM_COMMAND(recalc,"recalc","Recalculate syntax for the entire file.") {
  5140. for (int i = 0; i < env->line_count; ++i) {
  5141. env->lines[i]->istate = -1;
  5142. }
  5143. env->loading = 1;
  5144. for (int i = 0; i < env->line_count; ++i) {
  5145. recalculate_syntax(env->lines[i],i);
  5146. }
  5147. env->loading = 0;
  5148. redraw_all();
  5149. return 0;
  5150. }
  5151. BIM_COMMAND(tabs,"tabs","Use tabs for indentation") {
  5152. env->tabs = 1;
  5153. redraw_statusbar();
  5154. return 0;
  5155. }
  5156. BIM_COMMAND(spaces,"spaces","Use spaces for indentation") {
  5157. env->tabs = 0;
  5158. redraw_statusbar();
  5159. return 0;
  5160. }
  5161. BIM_COMMAND(tabstop,"tabstop","Show or set the tabstop (width of an indentation unit)") {
  5162. if (argc < 2) {
  5163. render_status_message("tabstop=%d", env->tabstop);
  5164. } else {
  5165. int t = atoi(argv[1]);
  5166. if (t > 0 && t < 32) {
  5167. env->tabstop = t;
  5168. for (int i = 0; i < env->line_count; ++i) {
  5169. recalculate_tabs(env->lines[i]);
  5170. }
  5171. redraw_all();
  5172. } else {
  5173. render_error("Invalid tabstop: %s", argv[1]);
  5174. }
  5175. }
  5176. return 0;
  5177. }
  5178. BIM_COMMAND(clearyear,"clearyank","Clear the yank buffer") {
  5179. if (global_config.yanks) {
  5180. for (unsigned int i = 0; i < global_config.yank_count; ++i) {
  5181. free(global_config.yanks[i]);
  5182. }
  5183. free(global_config.yanks);
  5184. global_config.yanks = NULL;
  5185. global_config.yank_count = 0;
  5186. redraw_statusbar();
  5187. }
  5188. return 0;
  5189. }
  5190. BIM_COMMAND(padding,"padding","Show or set cursor padding when scrolling vertically") {
  5191. if (argc < 2) {
  5192. render_status_message("padding=%d", global_config.cursor_padding);
  5193. } else {
  5194. global_config.cursor_padding = atoi(argv[1]);
  5195. if (env) {
  5196. place_cursor_actual();
  5197. }
  5198. }
  5199. return 0;
  5200. }
  5201. BIM_COMMAND(smartcase,"smartcase","Show or set the status of the smartcase search option") {
  5202. if (argc < 2) {
  5203. render_status_message("smartcase=%d", global_config.smart_case);
  5204. } else {
  5205. global_config.smart_case = atoi(argv[1]);
  5206. if (env) place_cursor_actual();
  5207. }
  5208. return 0;
  5209. }
  5210. BIM_COMMAND(hlparen,"hlparen","Show or set the configuration option to highlight matching braces") {
  5211. if (argc < 2) {
  5212. render_status_message("hlparen=%d", global_config.highlight_parens);
  5213. } else {
  5214. global_config.highlight_parens = atoi(argv[1]);
  5215. if (env) {
  5216. for (int i = 0; i < env->line_count; ++i) {
  5217. recalculate_syntax(env->lines[i],i);
  5218. }
  5219. redraw_text();
  5220. place_cursor_actual();
  5221. }
  5222. }
  5223. return 0;
  5224. }
  5225. BIM_COMMAND(hlcurrent,"hlcurrent","Show or set the configuration option to highlight the current line") {
  5226. if (argc < 2) {
  5227. render_status_message("hlcurrent=%d", global_config.highlight_current_line);
  5228. } else {
  5229. global_config.highlight_current_line = atoi(argv[1]);
  5230. if (env) {
  5231. if (!global_config.highlight_current_line) {
  5232. for (int i = 0; i < env->line_count; ++i) {
  5233. env->lines[i]->is_current = 0;
  5234. }
  5235. }
  5236. redraw_text();
  5237. place_cursor_actual();
  5238. }
  5239. }
  5240. return 0;
  5241. }
  5242. BIM_COMMAND(crnl,"crnl","Show or set the line ending mode") {
  5243. if (argc < 2) {
  5244. render_status_message("crnl=%d", env->crnl);
  5245. } else {
  5246. env->crnl = !!atoi(argv[1]);
  5247. redraw_statusbar();
  5248. }
  5249. return 0;
  5250. }
  5251. BIM_COMMAND(global_numbers,"global.numbers","Set whether numbers are displayed by default") {
  5252. if (argc < 2) {
  5253. render_status_message("global.numbers=%d", global_config.numbers);
  5254. } else {
  5255. global_config.numbers = !!atoi(argv[1]);
  5256. redraw_all();
  5257. }
  5258. return 0;
  5259. }
  5260. BIM_COMMAND(global_statusbar,"global.statusbar","Show or set whether to display the statusbar") {
  5261. if (argc < 2) {
  5262. render_status_message("global.statusbar=%d",!global_config.hide_statusbar);
  5263. } else {
  5264. global_config.hide_statusbar = !atoi(argv[1]);
  5265. global_config.bottom_size = global_config.hide_statusbar ? 1 : 2;
  5266. redraw_all();
  5267. }
  5268. return 0;
  5269. }
  5270. BIM_COMMAND(global_search_wraps,"wrapsearch","Enable search wrapping around from top or bottom") {
  5271. if (argc < 2) {
  5272. render_status_message("wrapsearch=%d",global_config.search_wraps);
  5273. } else {
  5274. global_config.search_wraps = !!atoi(argv[1]);
  5275. }
  5276. return 0;
  5277. }
  5278. BIM_COMMAND(smartcomplete,"smartcomplete","Enable autocompletion while typing") {
  5279. if (argc < 2) {
  5280. render_status_message("smartcomplete=%d",global_config.smart_complete);
  5281. } else {
  5282. global_config.smart_complete = !!atoi(argv[1]);
  5283. }
  5284. return 0;
  5285. }
  5286. BIM_COMMAND(global_autohide_tabs,"global.autohidetabs","Whether to show the tab bar when there is only one tab") {
  5287. if (argc < 2) {
  5288. render_status_message("global.autohidetabs=%d", global_config.autohide_tabs);
  5289. } else {
  5290. global_config.autohide_tabs = !!atoi(argv[1]);
  5291. global_config.tabs_visible = (!global_config.autohide_tabs) || (buffers_len > 1);
  5292. redraw_all();
  5293. }
  5294. return 0;
  5295. }
  5296. BIM_COMMAND(numbers,"numbers","Show or set the display of line numbers") {
  5297. if (argc < 2) {
  5298. render_status_message("numbers=%d", env->numbers);
  5299. } else {
  5300. env->numbers = !!atoi(argv[1]);
  5301. redraw_all();
  5302. }
  5303. return 0;
  5304. }
  5305. BIM_COMMAND(relativenumbers,"relativenumbers","Show or set the display of relative line numbers") {
  5306. if (argc < 2) {
  5307. render_status_message("relativenumber=%d", global_config.relative_lines);
  5308. } else {
  5309. global_config.relative_lines = atoi(argv[1]);
  5310. if (env) {
  5311. if (!global_config.relative_lines) {
  5312. for (int i = 0; i < env->line_count; ++i) {
  5313. env->lines[i]->is_current = 0;
  5314. }
  5315. }
  5316. redraw_text();
  5317. place_cursor_actual();
  5318. }
  5319. }
  5320. return 0;
  5321. }
  5322. BIM_COMMAND(buffers,"buffers","Show the open buffers") {
  5323. for (int i = 0; i < buffers_len; ++i) {
  5324. render_commandline_message("%d: %s\n", i, buffers[i]->file_name ? buffers[i]->file_name : "(no name)");
  5325. }
  5326. redraw_tabbar();
  5327. redraw_commandline();
  5328. pause_for_key();
  5329. return 0;
  5330. }
  5331. BIM_COMMAND(keyname,"keyname","Press and key and get its name.") {
  5332. int c;
  5333. render_commandline_message("(press a key)");
  5334. while ((c = bim_getkey(200)) == KEY_TIMEOUT);
  5335. render_commandline_message("%d = %s", c, name_from_key(c));
  5336. return 0;
  5337. }
  5338. /**
  5339. * Process a user command.
  5340. */
  5341. int process_command(char * cmd) {
  5342. if (*cmd == '#') return 0;
  5343. /* First, check prefix commands */
  5344. for (struct command_def * c = prefix_commands; prefix_commands && c->name; ++c) {
  5345. if (strstr(cmd, c->name) == cmd &&
  5346. (!isalpha(cmd[strlen(c->name)]) || !isalpha(cmd[0]))) {
  5347. return c->command(cmd, 0, NULL);
  5348. }
  5349. }
  5350. char * argv[3] = {NULL, NULL, NULL};
  5351. int argc = !!(*cmd);
  5352. char cmd_name[512] = {0};
  5353. for (char * c = cmd; *c; ++c) {
  5354. if (c-cmd == 511) break;
  5355. if (*c == ' ') {
  5356. cmd_name[c-cmd] = '\0';
  5357. argv[1] = c+1;
  5358. if (*argv[1]) argc++;
  5359. break;
  5360. }
  5361. cmd_name[c-cmd] = *c;
  5362. }
  5363. argv[0] = cmd_name;
  5364. argv[argc] = NULL;
  5365. if (argc < 1) {
  5366. /* no op */
  5367. return 0;
  5368. }
  5369. /* Now check regular commands */
  5370. for (struct command_def * c = regular_commands; regular_commands && c->name; ++c) {
  5371. if (!strcmp(argv[0], c->name)) {
  5372. return c->command(cmd, argc, argv);
  5373. }
  5374. }
  5375. global_config.break_from_selection = 1;
  5376. if (argv[0][0] == '-' && isdigit(argv[0][1])) {
  5377. goto_line(env->line_no-atoi(&argv[0][1]));
  5378. return 0;
  5379. } else if (argv[0][0] == '+' && isdigit(argv[0][1])) {
  5380. goto_line(env->line_no+atoi(&argv[0][1]));
  5381. return 0;
  5382. } else if (isdigit(*argv[0])) {
  5383. goto_line(atoi(argv[0]));
  5384. return 0;
  5385. } else {
  5386. render_error("Not an editor command: %s", argv[0]);
  5387. return 1;
  5388. }
  5389. }
  5390. /**
  5391. * Wrap strcmp for use with qsort.
  5392. */
  5393. int compare_str(const void * a, const void * b) {
  5394. return strcmp(*(const char **)a, *(const char **)b);
  5395. }
  5396. /**
  5397. * List of file extensions to ignore when tab completing.
  5398. */
  5399. const char * tab_complete_ignore[] = {".o",NULL};
  5400. /**
  5401. * Tab completion for command mode.
  5402. */
  5403. void command_tab_complete(char * buffer) {
  5404. /* Figure out which argument this is and where it starts */
  5405. int arg = 0;
  5406. char * buf = strdup(buffer);
  5407. char * b = buf;
  5408. char * args[32];
  5409. int candidate_count= 0;
  5410. int candidate_space = 4;
  5411. char ** candidates = malloc(sizeof(char*)*candidate_space);
  5412. /* Accept whitespace before first argument */
  5413. while (*b == ' ') b++;
  5414. char * start = b;
  5415. args[0] = start;
  5416. while (*b && *b != ' ') b++;
  5417. while (*b) {
  5418. while (*b == ' ') {
  5419. *b = '\0';
  5420. b++;
  5421. }
  5422. start = b;
  5423. arg++;
  5424. args[arg] = start;
  5425. break;
  5426. }
  5427. /**
  5428. * Check a possible candidate and add it to the
  5429. * candidates list, expanding as necessary,
  5430. * if it matches for the current argument.
  5431. */
  5432. #define add_candidate(candidate) \
  5433. do { \
  5434. char * _arg = args[arg]; \
  5435. int r = strncmp(_arg, candidate, strlen(_arg)); \
  5436. if (!r) { \
  5437. if (candidate_count == candidate_space) { \
  5438. candidate_space *= 2; \
  5439. candidates = realloc(candidates,sizeof(char *) * candidate_space); \
  5440. } \
  5441. candidates[candidate_count] = strdup(candidate); \
  5442. candidate_count++; \
  5443. } \
  5444. } while (0)
  5445. int _candidates_are_files = 0;
  5446. if (arg == 0 || (arg == 1 && !strcmp(args[0], "help"))) {
  5447. /* Complete command names */
  5448. for (struct command_def * c = regular_commands; regular_commands && c->name; ++c) {
  5449. add_candidate(c->name);
  5450. }
  5451. for (struct command_def * c = prefix_commands; prefix_commands && c->name; ++c) {
  5452. add_candidate(c->name);
  5453. }
  5454. goto _accept_candidate;
  5455. }
  5456. if (arg == 1 && !strcmp(args[0], "syntax")) {
  5457. /* Complete syntax options */
  5458. add_candidate("none");
  5459. for (struct syntax_definition * s = syntaxes; syntaxes && s->name; ++s) {
  5460. add_candidate(s->name);
  5461. }
  5462. goto _accept_candidate;
  5463. }
  5464. if (arg == 1 && (!strcmp(args[0], "theme") || !strcmp(args[0], "colorscheme"))) {
  5465. /* Complete color theme names */
  5466. for (struct theme_def * s = themes; themes && s->name; ++s) {
  5467. add_candidate(s->name);
  5468. }
  5469. goto _accept_candidate;
  5470. }
  5471. if (arg == 1 && (!strcmp(args[0], "setcolor"))) {
  5472. for (struct ColorName * c = color_names; c->name; ++c) {
  5473. add_candidate(c->name);
  5474. }
  5475. goto _accept_candidate;
  5476. }
  5477. if (arg == 1 && (!strcmp(args[0], "action"))) {
  5478. for (struct action_def * a = mappable_actions; a->name; ++a) {
  5479. add_candidate(a->name);
  5480. }
  5481. goto _accept_candidate;
  5482. }
  5483. if (arg == 1 && (!strcmp(args[0], "call") || !strcmp(args[0], "trycall") || !strcmp(args[0], "showfunction"))) {
  5484. for (int i = 0; i < flex_user_functions_count; ++i) {
  5485. add_candidate(user_functions[i]->command);
  5486. }
  5487. goto _accept_candidate;
  5488. }
  5489. if (arg == 1 && (!strcmp(args[0], "mapkey"))) {
  5490. for (int i = 0; args[arg][i]; ++i) {
  5491. if (args[arg][i] == ' ') {
  5492. while (args[arg][i] == ' ') {
  5493. args[arg][i] = '\0';
  5494. i++;
  5495. }
  5496. start = &args[arg][i];
  5497. arg++;
  5498. args[arg] = start;
  5499. i = 0;
  5500. if (arg == 32) {
  5501. arg = 31;
  5502. break;
  5503. }
  5504. }
  5505. }
  5506. if (arg == 1) {
  5507. for (struct mode_names * m = mode_names; m->name; ++m) {
  5508. add_candidate(m->name);
  5509. }
  5510. } else if (arg == 2) {
  5511. for (unsigned int i = 0; i < sizeof(KeyNames)/sizeof(KeyNames[0]); ++i) {
  5512. add_candidate(KeyNames[i].name);
  5513. }
  5514. } else if (arg == 3) {
  5515. for (struct action_def * a = mappable_actions; a->name; ++a) {
  5516. add_candidate(a->name);
  5517. }
  5518. add_candidate("none");
  5519. } else if (arg == 4) {
  5520. for (char * c = "racnwmb"; *c; ++c) {
  5521. char tmp[] = {*c,'\0'};
  5522. add_candidate(tmp);
  5523. }
  5524. }
  5525. goto _accept_candidate;
  5526. }
  5527. if (arg == 1 && (!strcmp(args[0], "e") || !strcmp(args[0], "tabnew") ||
  5528. !strcmp(args[0],"split") || !strcmp(args[0],"w") || !strcmp(args[0],"runscript") ||
  5529. !strcmp(args[0],"rundir") || args[0][0] == '!')) {
  5530. /* Complete file paths */
  5531. /* First, find the deepest directory match */
  5532. char * tmp = strdup(args[arg]);
  5533. char * last_slash = strrchr(tmp, '/');
  5534. DIR * dirp;
  5535. if (last_slash) {
  5536. *last_slash = '\0';
  5537. if (last_slash == tmp) {
  5538. /* Started with slash, and it was the only slash */
  5539. dirp = opendir("/");
  5540. } else {
  5541. char * home;
  5542. if (*tmp == '~' && (home = getenv("HOME"))) {
  5543. char * t = malloc(strlen(tmp) + strlen(home) + 4);
  5544. sprintf(t, "%s%s",home,tmp+1);
  5545. dirp = opendir(t);
  5546. free(t);
  5547. } else {
  5548. dirp = opendir(tmp);
  5549. }
  5550. }
  5551. } else {
  5552. /* No directory match, completing from current directory */
  5553. dirp = opendir(".");
  5554. tmp[0] = '\0';
  5555. }
  5556. if (!dirp) {
  5557. /* Directory match doesn't exist, no candidates to populate */
  5558. free(tmp);
  5559. goto done;
  5560. }
  5561. _candidates_are_files = 1;
  5562. struct dirent * ent = readdir(dirp);
  5563. while (ent != NULL) {
  5564. if (ent->d_name[0] != '.' || (last_slash ? (last_slash[1] == '.') : (tmp[0] == '.'))) {
  5565. struct stat statbuf;
  5566. /* Figure out if this file is a directory */
  5567. if (last_slash) {
  5568. char * x;
  5569. char * home;
  5570. if (tmp[0] == '~' && (home = getenv("HOME"))) {
  5571. x = malloc(strlen(tmp) + 1 + strlen(ent->d_name) + 1 + strlen(home) + 1);
  5572. snprintf(x, strlen(tmp) + 1 + strlen(ent->d_name) + 1 + strlen(home) + 1, "%s%s/%s",home,tmp+1,ent->d_name);
  5573. } else {
  5574. x = malloc(strlen(tmp) + 1 + strlen(ent->d_name) + 1);
  5575. snprintf(x, strlen(tmp) + 1 + strlen(ent->d_name) + 1, "%s/%s",tmp,ent->d_name);
  5576. }
  5577. stat(x, &statbuf);
  5578. free(x);
  5579. } else {
  5580. stat(ent->d_name, &statbuf);
  5581. }
  5582. /* Build the complete argument name to tab complete */
  5583. char s[1024] = {0};
  5584. if (last_slash == tmp) {
  5585. strcat(s,"/");
  5586. } else if (*tmp) {
  5587. strcat(s,tmp);
  5588. strcat(s,"/");
  5589. }
  5590. strcat(s,ent->d_name);
  5591. /*
  5592. * If it is a directory, add a / to the end so the next completion
  5593. * attempt will complete the directory's contents.
  5594. */
  5595. if (S_ISDIR(statbuf.st_mode)) {
  5596. strcat(s,"/");
  5597. }
  5598. int skip = 0;
  5599. for (const char ** c = tab_complete_ignore; *c; ++c) {
  5600. if (str_ends_with(s, *c)) {
  5601. skip = 1;
  5602. break;
  5603. }
  5604. }
  5605. if (!skip) {
  5606. add_candidate(s);
  5607. }
  5608. }
  5609. ent = readdir(dirp);
  5610. }
  5611. closedir(dirp);
  5612. free(tmp);
  5613. goto _accept_candidate;
  5614. }
  5615. _accept_candidate:
  5616. if (candidate_count == 0) {
  5617. redraw_statusbar();
  5618. goto done;
  5619. }
  5620. if (candidate_count == 1) {
  5621. /* Only one completion possibility */
  5622. redraw_statusbar();
  5623. /* Fill out the rest of the command */
  5624. char * cstart = (buffer) + (start - buf);
  5625. for (unsigned int i = 0; i < strlen(candidates[0]); ++i) {
  5626. *cstart = candidates[0][i];
  5627. cstart++;
  5628. }
  5629. *cstart = '\0';
  5630. } else {
  5631. /* Sort candidates */
  5632. qsort(candidates, candidate_count, sizeof(candidates[0]), compare_str);
  5633. /* Print candidates in status bar */
  5634. char * tmp = malloc(global_config.term_width+1);
  5635. memset(tmp, 0, global_config.term_width+1);
  5636. int offset = 0;
  5637. for (int i = 0; i < candidate_count; ++i) {
  5638. char * printed_candidate = candidates[i];
  5639. if (_candidates_are_files) {
  5640. for (char * c = printed_candidate; *c; ++c) {
  5641. if (c[0] == '/' && c[1] != '\0') {
  5642. printed_candidate = c+1;
  5643. }
  5644. }
  5645. }
  5646. if (offset + 1 + (signed)strlen(printed_candidate) > global_config.term_width - 5) {
  5647. strcat(tmp, "...");
  5648. break;
  5649. }
  5650. if (offset > 0) {
  5651. strcat(tmp, " ");
  5652. offset++;
  5653. }
  5654. strcat(tmp, printed_candidate);
  5655. offset += strlen(printed_candidate);
  5656. }
  5657. render_status_message("%s", tmp);
  5658. free(tmp);
  5659. /* Complete to longest common substring */
  5660. char * cstart = (buffer) + (start - buf);
  5661. for (int i = 0; i < 1023 /* max length of command */; i++) {
  5662. for (int j = 1; j < candidate_count; ++j) {
  5663. if (candidates[0][i] != candidates[j][i]) goto _reject;
  5664. }
  5665. *cstart = candidates[0][i];
  5666. cstart++;
  5667. }
  5668. /* End of longest common substring */
  5669. _reject:
  5670. *cstart = '\0';
  5671. /* Just make sure the buffer doesn't end on an incomplete multibyte sequence */
  5672. if (start > buf) { /* Start point needs to be something other than first byte */
  5673. char * tmp = cstart - 1;
  5674. if ((*tmp & 0xC0) == 0x80) {
  5675. /* Count back until we find the start byte and make sure we have the right number */
  5676. int count = 1;
  5677. int x = 0;
  5678. while (tmp >= start) {
  5679. x++;
  5680. tmp--;
  5681. if ((*tmp & 0xC0) == 0x80) {
  5682. count++;
  5683. } else if ((*tmp & 0xC0) == 0xC0) {
  5684. /* How many should we have? */
  5685. int i = 1;
  5686. int j = *tmp;
  5687. while (j & 0x20) {
  5688. i += 1;
  5689. j <<= 1;
  5690. }
  5691. if (count != i) {
  5692. *tmp = '\0';
  5693. break;
  5694. }
  5695. break;
  5696. } else {
  5697. /* This isn't right, we had a bad multibyte sequence? Or someone is typing Latin-1. */
  5698. tmp++;
  5699. *tmp = '\0';
  5700. break;
  5701. }
  5702. }
  5703. } else if ((*tmp & 0xC0) == 0xC0) {
  5704. *tmp = '\0';
  5705. }
  5706. }
  5707. }
  5708. /* Free candidates */
  5709. for (int i = 0; i < candidate_count; ++i) {
  5710. free(candidates[i]);
  5711. }
  5712. done:
  5713. free(candidates);
  5714. free(buf);
  5715. }
  5716. /**
  5717. * Macros for use in command mode.
  5718. */
  5719. #define _syn_command() do { env->syntax = global_config.command_syn; } while (0)
  5720. #define _syn_restore() do { env->syntax = global_config.command_syn_back; } while (0)
  5721. #define _restore_history(point) do { \
  5722. unsigned char * t = command_history[point]; \
  5723. global_config.command_col_no = 1; \
  5724. global_config.command_buffer->actual = 0; \
  5725. _syn_command(); \
  5726. uint32_t state = 0; \
  5727. uint32_t c = 0; \
  5728. while (*t) { \
  5729. if (!decode(&state, &c, *t)) { \
  5730. char_t _c = {codepoint_width(c), 0, c}; \
  5731. global_config.command_buffer = line_insert(global_config.command_buffer, _c, global_config.command_col_no - 1, -1); \
  5732. global_config.command_col_no++; \
  5733. } else if (state == UTF8_REJECT) state = 0; \
  5734. t++; \
  5735. } \
  5736. _syn_restore(); \
  5737. } while (0)
  5738. /**
  5739. * Draw the command buffer and any prefix.
  5740. */
  5741. void render_command_input_buffer(void) {
  5742. if (!global_config.command_buffer) return;
  5743. /* Place the cursor at the bottom of the screen */
  5744. place_cursor(1, global_config.term_height);
  5745. paint_line(COLOR_BG);
  5746. set_colors(COLOR_ALT_FG, COLOR_BG);
  5747. /* If there's a mode name to render, draw it first */
  5748. int _left_gutter = 0;
  5749. if (env->mode == MODE_LINE_SELECTION) {
  5750. _left_gutter = printf("(LINE %d:%d)",
  5751. (env->start_line < env->line_no) ? env->start_line : env->line_no,
  5752. (env->start_line < env->line_no) ? env->line_no : env->start_line);
  5753. } else if (env->mode == MODE_COL_SELECTION) {
  5754. _left_gutter = printf("(COL %d:%d %d)",
  5755. (env->start_line < env->line_no) ? env->start_line : env->line_no,
  5756. (env->start_line < env->line_no) ? env->line_no : env->start_line,
  5757. (env->sel_col));
  5758. } else if (env->mode == MODE_CHAR_SELECTION) {
  5759. _left_gutter = printf("(CHAR)");
  5760. }
  5761. /* Figure out the cursor position and adjust the offset if necessary */
  5762. int x = 2 + _left_gutter - global_config.command_offset;
  5763. for (int i = 0; i < global_config.command_col_no - 1; ++i) {
  5764. char_t * c = &global_config.command_buffer->text[i];
  5765. x += c->display_width;
  5766. }
  5767. if (x > global_config.term_width - 1) {
  5768. int diff = x - (global_config.term_width - 1);
  5769. global_config.command_offset += diff;
  5770. x -= diff;
  5771. }
  5772. if (x < 2 + _left_gutter) {
  5773. int diff = (2 + _left_gutter) - x;
  5774. global_config.command_offset -= diff;
  5775. x += diff;
  5776. }
  5777. /* If the input buffer is horizontally shifted because it's too long, indicate that. */
  5778. if (global_config.command_offset) {
  5779. set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
  5780. printf("<");
  5781. } else {
  5782. /* Otherwise indicate buffer mode (search / ?, or command :) */
  5783. set_colors(COLOR_FG, COLOR_BG);
  5784. if (global_config.overlay_mode == OVERLAY_MODE_SEARCH) {
  5785. printf(global_config.search_direction == 0 ? "?" : "/");
  5786. } else {
  5787. printf(":");
  5788. }
  5789. }
  5790. /* Render the buffer */
  5791. render_line(global_config.command_buffer, global_config.term_width-1-_left_gutter, global_config.command_offset, -1);
  5792. /* Place and display the cursor */
  5793. place_cursor(x, global_config.term_height);
  5794. show_cursor();
  5795. }
  5796. BIM_ACTION(command_discard, 0,
  5797. "Discard the input buffer and cancel command or search."
  5798. )(void) {
  5799. free(global_config.command_buffer);
  5800. global_config.command_buffer = NULL;
  5801. if (global_config.overlay_mode == OVERLAY_MODE_SEARCH) {
  5802. env->line_no = global_config.prev_line;
  5803. env->col_no = global_config.prev_col;
  5804. /* Unhighlight search matches */
  5805. for (int i = 0; i < env->line_count; ++i) {
  5806. for (int j = 0; j < env->lines[i]->actual; ++j) {
  5807. env->lines[i]->text[j].flags &= (~FLAG_SEARCH);
  5808. }
  5809. rehighlight_search(env->lines[i]);
  5810. }
  5811. }
  5812. global_config.overlay_mode = OVERLAY_MODE_NONE;
  5813. redraw_all();
  5814. /* TODO exit some other modes? */
  5815. }
  5816. BIM_ACTION(enter_command, 0,
  5817. "Enter command input mode."
  5818. )(void) {
  5819. global_config.overlay_mode = OVERLAY_MODE_COMMAND;
  5820. global_config.command_offset = 0;
  5821. global_config.command_col_no = 1;
  5822. if (global_config.command_buffer) {
  5823. free(global_config.command_buffer);
  5824. }
  5825. global_config.command_buffer = calloc(sizeof(line_t)+sizeof(char_t)*32,1);
  5826. global_config.command_buffer->available = 32;
  5827. global_config.command_syn_back = env->syntax;
  5828. global_config.command_syn = find_syntax_calculator("bimcmd");
  5829. global_config.history_point = -1;
  5830. render_command_input_buffer();
  5831. }
  5832. BIM_ACTION(command_accept, 0,
  5833. "Accept the command input and run the requested command."
  5834. )(void) {
  5835. /* Convert command buffer to UTF-8 char-array string */
  5836. size_t size = 0;
  5837. for (int i = 0; i < global_config.command_buffer->actual; ++i) {
  5838. char tmp[8] = {0};
  5839. size += to_eight(global_config.command_buffer->text[i].codepoint, tmp);
  5840. }
  5841. char * tmp = malloc(size + 8); /* for overflow from to_eight */
  5842. char * t = tmp;
  5843. for (int i = 0; i < global_config.command_buffer->actual; ++i) {
  5844. t += to_eight(global_config.command_buffer->text[i].codepoint, t);
  5845. }
  5846. *t = '\0';
  5847. /* Free the original editing buffer */
  5848. free(global_config.command_buffer);
  5849. global_config.command_buffer = NULL;
  5850. /* Run the converted command */
  5851. global_config.break_from_selection = 0;
  5852. insert_command_history(tmp);
  5853. process_command(tmp);
  5854. free(tmp);
  5855. if (!global_config.break_from_selection && env->mode != MODE_DIRECTORY_BROWSE) {
  5856. if (env->mode == MODE_LINE_SELECTION || env->mode == MODE_CHAR_SELECTION || env->mode == MODE_COL_SELECTION) {
  5857. recalculate_selected_lines();
  5858. }
  5859. env->mode = MODE_NORMAL;
  5860. }
  5861. /* Leave command mode */
  5862. global_config.overlay_mode = OVERLAY_MODE_NONE;
  5863. }
  5864. BIM_ACTION(command_word_delete, 0,
  5865. "Delete the previous word from the input buffer."
  5866. )(void) {
  5867. _syn_command();
  5868. while (global_config.command_col_no > 1 &&
  5869. (global_config.command_buffer->text[global_config.command_col_no-2].codepoint == ' ' ||
  5870. global_config.command_buffer->text[global_config.command_col_no-2].codepoint == '/')) {
  5871. line_delete(global_config.command_buffer, global_config.command_col_no - 1, -1);
  5872. global_config.command_col_no--;
  5873. }
  5874. while (global_config.command_col_no > 1 &&
  5875. global_config.command_buffer->text[global_config.command_col_no-2].codepoint != ' ' &&
  5876. global_config.command_buffer->text[global_config.command_col_no-2].codepoint != '/') {
  5877. line_delete(global_config.command_buffer, global_config.command_col_no - 1, -1);
  5878. global_config.command_col_no--;
  5879. }
  5880. _syn_restore();
  5881. }
  5882. BIM_ACTION(command_tab_complete_buffer, 0,
  5883. "Complete command names and arguments in the input buffer."
  5884. )(void) {
  5885. /* command_tab_complete should probably just be adjusted to deal with the buffer... */
  5886. char * tmp = calloc(1024,1);
  5887. char * t = tmp;
  5888. for (int i = 0; i < global_config.command_col_no-1; ++i) {
  5889. t += to_eight(global_config.command_buffer->text[i].codepoint, t);
  5890. }
  5891. *t = '\0';
  5892. _syn_command();
  5893. while (global_config.command_col_no > 1) {
  5894. line_delete(global_config.command_buffer, global_config.command_col_no - 1, -1);
  5895. global_config.command_col_no--;
  5896. }
  5897. _syn_restore();
  5898. command_tab_complete(tmp);
  5899. _syn_command();
  5900. uint32_t state = 0, c= 0;
  5901. t = tmp;
  5902. while (*t) {
  5903. if (!decode(&state, &c, *t)) {
  5904. char_t _c = {codepoint_width(c), 0, c};
  5905. global_config.command_buffer = line_insert(global_config.command_buffer, _c, global_config.command_col_no - 1, -1);
  5906. global_config.command_col_no++;
  5907. }
  5908. t++;
  5909. }
  5910. _syn_restore();
  5911. free(tmp);
  5912. }
  5913. BIM_ACTION(command_backspace, 0,
  5914. "Erase the character before the cursor in the input buffer."
  5915. )(void) {
  5916. if (global_config.command_col_no <= 1) {
  5917. if (global_config.command_buffer->actual == 0) {
  5918. command_discard();
  5919. }
  5920. return;
  5921. }
  5922. _syn_command();
  5923. line_delete(global_config.command_buffer, global_config.command_col_no - 1, -1);
  5924. _syn_restore();
  5925. global_config.command_col_no--;
  5926. global_config.command_offset = 0;
  5927. }
  5928. BIM_ACTION(command_scroll_history, ARG_IS_CUSTOM,
  5929. "Scroll through command input history."
  5930. )(int direction) {
  5931. if (direction == -1) {
  5932. if (command_history[global_config.history_point+1]) {
  5933. _restore_history(global_config.history_point+1);
  5934. global_config.history_point++;
  5935. }
  5936. } else {
  5937. if (global_config.history_point > 0) {
  5938. global_config.history_point--;
  5939. _restore_history(global_config.history_point);
  5940. } else {
  5941. global_config.history_point = -1;
  5942. global_config.command_col_no = 1;
  5943. global_config.command_buffer->actual = 0;
  5944. }
  5945. }
  5946. }
  5947. BIM_ACTION(command_word_left, 0,
  5948. "Move to the start of the previous word in the input buffer."
  5949. )(void) {
  5950. if (global_config.command_col_no > 1) {
  5951. do {
  5952. global_config.command_col_no--;
  5953. } while (isspace(global_config.command_buffer->text[global_config.command_col_no-1].codepoint) && global_config.command_col_no > 1);
  5954. if (global_config.command_col_no == 1) return;
  5955. do {
  5956. global_config.command_col_no--;
  5957. } while (!isspace(global_config.command_buffer->text[global_config.command_col_no-1].codepoint) && global_config.command_col_no > 1);
  5958. if (isspace(global_config.command_buffer->text[global_config.command_col_no-1].codepoint) && global_config.command_col_no < global_config.command_buffer->actual) global_config.command_col_no++;
  5959. }
  5960. }
  5961. BIM_ACTION(command_word_right, 0,
  5962. "Move to the start of the next word in the input buffer."
  5963. )(void) {
  5964. if (global_config.command_col_no < global_config.command_buffer->actual) {
  5965. do {
  5966. global_config.command_col_no++;
  5967. if (global_config.command_col_no > global_config.command_buffer->actual) { global_config.command_col_no = global_config.command_buffer->actual+1; break; }
  5968. } while (!isspace(global_config.command_buffer->text[global_config.command_col_no-1].codepoint) && global_config.command_col_no <= global_config.command_buffer->actual);
  5969. do {
  5970. global_config.command_col_no++;
  5971. if (global_config.command_col_no > global_config.command_buffer->actual) { global_config.command_col_no = global_config.command_buffer->actual+1; break; }
  5972. } while (isspace(global_config.command_buffer->text[global_config.command_col_no-1].codepoint) && global_config.command_col_no <= global_config.command_buffer->actual);
  5973. if (global_config.command_col_no > global_config.command_buffer->actual) { global_config.command_col_no = global_config.command_buffer->actual+1; }
  5974. }
  5975. }
  5976. BIM_ACTION(command_cursor_left, 0,
  5977. "Move the cursor one character left in the input buffer."
  5978. )(void) {
  5979. if (global_config.command_col_no > 1) global_config.command_col_no--;
  5980. }
  5981. BIM_ACTION(command_cursor_right, 0,
  5982. "Move the cursor one character right in the input buffer."
  5983. )(void) {
  5984. if (global_config.command_col_no < global_config.command_buffer->actual+1) global_config.command_col_no++;
  5985. }
  5986. BIM_ACTION(command_cursor_home, 0,
  5987. "Move the cursor to the start of the input buffer."
  5988. )(void) {
  5989. global_config.command_col_no = 1;
  5990. }
  5991. BIM_ACTION(command_cursor_end, 0,
  5992. "Move the cursor to the end of the input buffer."
  5993. )(void) {
  5994. global_config.command_col_no = global_config.command_buffer->actual + 1;
  5995. }
  5996. BIM_ACTION(eat_mouse, 0,
  5997. "(temporary) Read, but ignore mouse input."
  5998. )(void) {
  5999. bim_getch();
  6000. bim_getch();
  6001. bim_getch();
  6002. }
  6003. BIM_ACTION(command_insert_char, ARG_IS_INPUT,
  6004. "Insert one character into the input buffer."
  6005. )(int c) {
  6006. char_t _c = {codepoint_width(c), 0, c};
  6007. _syn_command();
  6008. global_config.command_buffer = line_insert(global_config.command_buffer, _c, global_config.command_col_no - 1, -1);
  6009. _syn_restore();
  6010. global_config.command_col_no++;
  6011. }
  6012. /**
  6013. * Determine whether a string should be searched
  6014. * case-sensitive or not based on whether it contains
  6015. * any upper-case letters.
  6016. */
  6017. int smart_case(uint32_t * str) {
  6018. if (!global_config.smart_case) return 0;
  6019. for (uint32_t * s = str; *s; ++s) {
  6020. if (tolower(*s) != (int)*s) {
  6021. return 0;
  6022. }
  6023. }
  6024. return 1;
  6025. }
  6026. /**
  6027. * Search forward from the given cursor position
  6028. * to find a basic search match.
  6029. *
  6030. * This could be more complicated...
  6031. */
  6032. void find_match(int from_line, int from_col, int * out_line, int * out_col, uint32_t * str, int * matchlen) {
  6033. int col = from_col;
  6034. int ignorecase = smart_case(str);
  6035. for (int i = from_line; i <= env->line_count; ++i) {
  6036. line_t * line = env->lines[i - 1];
  6037. int j = col - 1;
  6038. while (j < line->actual + 1) {
  6039. if (subsearch_matches(line, j, str, ignorecase, matchlen)) {
  6040. *out_line = i;
  6041. *out_col = j + 1;
  6042. return;
  6043. }
  6044. j++;
  6045. }
  6046. col = 0;
  6047. }
  6048. }
  6049. /**
  6050. * Search backwards for matching string.
  6051. */
  6052. void find_match_backwards(int from_line, int from_col, int * out_line, int * out_col, uint32_t * str) {
  6053. int col = from_col;
  6054. int ignorecase = smart_case(str);
  6055. for (int i = from_line; i >= 1; --i) {
  6056. line_t * line = env->lines[i-1];
  6057. int j = col - 1;
  6058. while (j > -1) {
  6059. if (subsearch_matches(line, j, str, ignorecase, NULL)) {
  6060. *out_line = i;
  6061. *out_col = j + 1;
  6062. return;
  6063. }
  6064. j--;
  6065. }
  6066. col = (i > 1) ? (env->lines[i-2]->actual + 1) : -1;
  6067. }
  6068. }
  6069. /**
  6070. * Re-mark search matches while editing text.
  6071. * This gets called after recalculate_syntax, so it works as we're typing or
  6072. * whenever syntax calculation would redraw other lines.
  6073. * XXX There's a bunch of code duplication between the (now three) search match functions.
  6074. * and search should be improved to support regex anyway, so this needs to be fixed.
  6075. */
  6076. void rehighlight_search(line_t * line) {
  6077. if (!global_config.search) return;
  6078. int j = 0;
  6079. int ignorecase = smart_case(global_config.search);
  6080. while (j < line->actual) {
  6081. int matchlen = 0;
  6082. if (subsearch_matches(line, j, global_config.search, ignorecase, &matchlen)) {
  6083. for (int i = j; matchlen > 0; ++i, matchlen--) {
  6084. line->text[i].flags |= FLAG_SEARCH;
  6085. }
  6086. }
  6087. j++;
  6088. }
  6089. }
  6090. /**
  6091. * Draw the matched search result.
  6092. */
  6093. void draw_search_match(uint32_t * buffer, int redraw_buffer) {
  6094. for (int i = 0; i < env->line_count; ++i) {
  6095. for (int j = 0; j < env->lines[i]->actual; ++j) {
  6096. env->lines[i]->text[j].flags &= (~FLAG_SEARCH);
  6097. }
  6098. }
  6099. int line = -1, col = -1, _line = 1, _col = 1;
  6100. do {
  6101. int matchlen;
  6102. find_match(_line, _col, &line, &col, buffer, &matchlen);
  6103. if (line != -1) {
  6104. line_t * l = env->lines[line-1];
  6105. for (int i = col; matchlen > 0; ++i, --matchlen) {
  6106. l->text[i-1].flags |= FLAG_SEARCH;
  6107. }
  6108. }
  6109. _line = line;
  6110. _col = col+1;
  6111. line = -1;
  6112. col = -1;
  6113. } while (_line != -1);
  6114. redraw_text();
  6115. place_cursor_actual();
  6116. redraw_statusbar();
  6117. redraw_commandline();
  6118. if (redraw_buffer != -1) {
  6119. printf(redraw_buffer == 1 ? "/" : "?");
  6120. uint32_t * c = buffer;
  6121. while (*c) {
  6122. char tmp[7] = {0}; /* Max six bytes, use 7 to ensure last is always nil */
  6123. to_eight(*c, tmp);
  6124. printf("%s", tmp);
  6125. c++;
  6126. }
  6127. }
  6128. }
  6129. BIM_ACTION(enter_search, ARG_IS_CUSTOM,
  6130. "Enter search mode."
  6131. )(int direction) {
  6132. global_config.overlay_mode = OVERLAY_MODE_SEARCH;
  6133. global_config.command_offset = 0;
  6134. global_config.command_col_no = 1;
  6135. global_config.prev_line = env->line_no;
  6136. global_config.prev_col = env->col_no;
  6137. global_config.prev_coffset = env->coffset;
  6138. global_config.prev_offset = env->offset;
  6139. global_config.search_direction = direction;
  6140. if (global_config.command_buffer) {
  6141. free(global_config.command_buffer);
  6142. }
  6143. global_config.command_buffer = calloc(sizeof(line_t)+sizeof(char_t)*32,1);
  6144. global_config.command_buffer->available = 32;
  6145. global_config.command_syn_back = env->syntax;
  6146. global_config.command_syn = NULL; /* Disable syntax highlighting in search; maybe use buffer's mode instead? */
  6147. render_command_input_buffer();
  6148. }
  6149. BIM_ACTION(search_accept, 0,
  6150. "Accept the search term and return to the previous mode."
  6151. )(void) {
  6152. /* Store the accepted search */
  6153. if (!global_config.command_buffer->actual) {
  6154. if (global_config.search) {
  6155. search_next();
  6156. }
  6157. goto _finish;
  6158. }
  6159. if (global_config.search) {
  6160. free(global_config.search);
  6161. }
  6162. global_config.search = malloc((global_config.command_buffer->actual + 1) * sizeof(uint32_t));
  6163. for (int i = 0; i < global_config.command_buffer->actual; ++i) {
  6164. global_config.search[i] = global_config.command_buffer->text[i].codepoint;
  6165. }
  6166. global_config.search[global_config.command_buffer->actual] = 0;
  6167. _finish:
  6168. /* Free the original editing buffer */
  6169. free(global_config.command_buffer);
  6170. global_config.command_buffer = NULL;
  6171. /* Leave command mode */
  6172. global_config.overlay_mode = OVERLAY_MODE_NONE;
  6173. }
  6174. /**
  6175. * Find the next search result, or loop back around if at the end.
  6176. */
  6177. BIM_ACTION(search_next, 0,
  6178. "Jump to the next search match."
  6179. )(void) {
  6180. if (!global_config.search) return;
  6181. if (env->coffset) env->coffset = 0;
  6182. int line = -1, col = -1;
  6183. find_match(env->line_no, env->col_no+1, &line, &col, global_config.search, NULL);
  6184. if (line == -1) {
  6185. if (!global_config.search_wraps) return;
  6186. find_match(1,1, &line, &col, global_config.search, NULL);
  6187. if (line == -1) return;
  6188. }
  6189. env->col_no = col;
  6190. env->line_no = line;
  6191. set_preferred_column();
  6192. draw_search_match(global_config.search, -1);
  6193. }
  6194. /**
  6195. * Find the previous search result, or loop to the end of the file.
  6196. */
  6197. BIM_ACTION(search_prev, 0,
  6198. "Jump to the preceding search match."
  6199. )(void) {
  6200. if (!global_config.search) return;
  6201. if (env->coffset) env->coffset = 0;
  6202. int line = -1, col = -1;
  6203. find_match_backwards(env->line_no, env->col_no-1, &line, &col, global_config.search);
  6204. if (line == -1) {
  6205. if (!global_config.search_wraps) return;
  6206. find_match_backwards(env->line_count, env->lines[env->line_count-1]->actual, &line, &col, global_config.search);
  6207. if (line == -1) return;
  6208. }
  6209. env->col_no = col;
  6210. env->line_no = line;
  6211. set_preferred_column();
  6212. draw_search_match(global_config.search, -1);
  6213. }
  6214. /**
  6215. * Find the matching paren for this one.
  6216. *
  6217. * This approach skips having to do its own syntax parsing
  6218. * to deal with, eg., erroneous parens in comments. It does
  6219. * this by finding the matching paren with the same flag
  6220. * value, thus parens in strings will match, parens outside
  6221. * of strings will match, but parens in strings won't
  6222. * match parens outside of strings and so on.
  6223. */
  6224. void find_matching_paren(int * out_line, int * out_col, int in_col) {
  6225. if (env->col_no - in_col + 1 > env->lines[env->line_no-1]->actual) {
  6226. return; /* Invalid cursor position */
  6227. }
  6228. /* TODO: vim can find the nearest paren to start searching from, we need to be on one right now */
  6229. int paren_match = 0;
  6230. int direction = 0;
  6231. int start = env->lines[env->line_no-1]->text[env->col_no-in_col].codepoint;
  6232. int flags = env->lines[env->line_no-1]->text[env->col_no-in_col].flags & 0x1F;
  6233. int count = 0;
  6234. /* TODO what about unicode parens? */
  6235. for (int i = 0; paren_pairs[i]; ++i) {
  6236. if (start == paren_pairs[i]) {
  6237. direction = (i % 2 == 0) ? 1 : -1;
  6238. paren_match = paren_pairs[(i % 2 == 0) ? (i+1) : (i-1)];
  6239. break;
  6240. }
  6241. }
  6242. if (!paren_match) return;
  6243. /* Scan for match */
  6244. int line = env->line_no;
  6245. int col = env->col_no - in_col + 1;
  6246. do {
  6247. while (col > 0 && col < env->lines[line-1]->actual + 1) {
  6248. /* Only match on same syntax */
  6249. if ((env->lines[line-1]->text[col-1].flags & 0x1F) == flags) {
  6250. /* Count up on same direction */
  6251. if (env->lines[line-1]->text[col-1].codepoint == start) count++;
  6252. /* Count down on opposite direction */
  6253. if (env->lines[line-1]->text[col-1].codepoint == paren_match) {
  6254. count--;
  6255. /* When count == 0 we have a match */
  6256. if (count == 0) goto _match_found;
  6257. }
  6258. }
  6259. col += direction;
  6260. }
  6261. line += direction;
  6262. /* Reached first/last line with no match */
  6263. if (line == 0 || line == env->line_count + 1) {
  6264. return;
  6265. }
  6266. /* Reset column to start/end of line, depending on direction */
  6267. if (direction > 0) {
  6268. col = 1;
  6269. } else {
  6270. col = env->lines[line-1]->actual;
  6271. }
  6272. } while (1);
  6273. _match_found:
  6274. *out_line = line;
  6275. *out_col = col;
  6276. }
  6277. /**
  6278. * Switch to the left split view
  6279. * (Primarily to handle cases where the left and right are the same buffer)
  6280. */
  6281. BIM_ACTION(use_left_buffer, 0,
  6282. "Switch to the left split view."
  6283. )(void) {
  6284. if (left_buffer == right_buffer && env->left != 0) {
  6285. view_right_offset = env->offset;
  6286. env->width = env->left;
  6287. env->left = 0;
  6288. env->offset = view_left_offset;
  6289. }
  6290. env = left_buffer;
  6291. update_title();
  6292. }
  6293. /**
  6294. * Switch to the right split view
  6295. * (Primarily to handle cases where the left and right are the same buffer)
  6296. */
  6297. BIM_ACTION(use_right_buffer, 0,
  6298. "Switch to the right split view."
  6299. )(void) {
  6300. if (left_buffer == right_buffer && env->left == 0) {
  6301. view_left_offset = env->offset;
  6302. env->left = env->width;
  6303. env->width = global_config.term_width - env->width;
  6304. env->offset = view_right_offset;
  6305. }
  6306. env = right_buffer;
  6307. update_title();
  6308. }
  6309. void handle_common_mouse(int buttons, int x, int y) {
  6310. if (buttons == 64) {
  6311. /* Scroll up */
  6312. if (global_config.shift_scrolling) {
  6313. env->loading = 1;
  6314. int shifted = 0;
  6315. for (int i = 0; i < global_config.scroll_amount; ++i) {
  6316. if (env->offset > 0) {
  6317. env->offset--;
  6318. if (env->line_no > env->offset + global_config.term_height - global_config.bottom_size - global_config.tabs_visible - global_config.cursor_padding) {
  6319. cursor_up();
  6320. }
  6321. shifted++;
  6322. }
  6323. }
  6324. env->loading = 0;
  6325. if (!shifted) return;
  6326. if (global_config.can_scroll && !left_buffer) {
  6327. if (!global_config.can_insert) {
  6328. shift_down(shifted);
  6329. redraw_tabbar();
  6330. } else {
  6331. insert_lines_at(global_config.tabs_visible ? 2 : 1, shifted);
  6332. }
  6333. for (int i = 0; i < shifted; ++i) {
  6334. redraw_line(env->offset+i);
  6335. }
  6336. } else {
  6337. redraw_tabbar();
  6338. redraw_text();
  6339. }
  6340. redraw_statusbar();
  6341. redraw_commandline();
  6342. place_cursor_actual();
  6343. } else {
  6344. for (int i = 0; i < global_config.scroll_amount; ++i) {
  6345. cursor_up();
  6346. }
  6347. }
  6348. return;
  6349. } else if (buttons == 65) {
  6350. /* Scroll down */
  6351. if (global_config.shift_scrolling) {
  6352. env->loading = 1;
  6353. int shifted = 0;
  6354. for (int i = 0; i < global_config.scroll_amount; ++i) {
  6355. if (env->offset < env->line_count-1) {
  6356. env->offset++;
  6357. int e = (env->offset == 0) ? env->offset : env->offset + global_config.cursor_padding;
  6358. if (env->line_no <= e) {
  6359. cursor_down();
  6360. }
  6361. shifted++;
  6362. }
  6363. }
  6364. env->loading = 0;
  6365. if (!shifted) return;
  6366. if (global_config.can_scroll && !left_buffer) {
  6367. if (!global_config.can_insert) {
  6368. shift_up(shifted);
  6369. redraw_tabbar();
  6370. } else {
  6371. delete_lines_at(global_config.tabs_visible ? 2 : 1, shifted);
  6372. }
  6373. int l = global_config.term_height - global_config.bottom_size - global_config.tabs_visible;
  6374. for (int i = 0; i < shifted; ++i) {
  6375. if (env->offset + l - i < env->line_count + 1) {
  6376. redraw_line(env->offset + l-1-i);
  6377. } else {
  6378. draw_excess_line(l - 1 - i);
  6379. }
  6380. }
  6381. } else {
  6382. redraw_tabbar();
  6383. redraw_text();
  6384. }
  6385. redraw_statusbar();
  6386. redraw_commandline();
  6387. place_cursor_actual();
  6388. } else {
  6389. for (int i = 0; i < global_config.scroll_amount; ++i) {
  6390. cursor_down();
  6391. }
  6392. }
  6393. return;
  6394. } else if (buttons == 3) {
  6395. /* Move cursor to position */
  6396. if (x < 0) return;
  6397. if (y < 0) return;
  6398. if (y == 1 && global_config.tabs_visible) {
  6399. /* Pick from tabs */
  6400. if (env->mode != MODE_NORMAL && env->mode != MODE_INSERT) return; /* Don't let the tab be switched in other modes for now */
  6401. int _x = 0;
  6402. if (global_config.tab_offset) _x = 1;
  6403. if (global_config.tab_offset && _x >= x) {
  6404. global_config.tab_offset--;
  6405. redraw_tabbar();
  6406. return;
  6407. }
  6408. for (int i = global_config.tab_offset; i < buffers_len; i++) {
  6409. buffer_t * _env = buffers[i];
  6410. char tmp[64];
  6411. int size = 0;
  6412. int filled = draw_tab_name(_env, tmp, global_config.term_width - _x, &size);
  6413. _x += size;
  6414. if (_x >= x) {
  6415. if (left_buffer && buffers[i] != left_buffer && buffers[i] != right_buffer) unsplit();
  6416. env = buffers[i];
  6417. redraw_all();
  6418. update_title();
  6419. return;
  6420. }
  6421. if (filled) break;
  6422. }
  6423. if (x > _x && global_config.tab_offset < buffers_len - 1) {
  6424. global_config.tab_offset++;
  6425. redraw_tabbar();
  6426. }
  6427. return;
  6428. }
  6429. if (env->mode == MODE_NORMAL || env->mode == MODE_INSERT) {
  6430. int current_mode = env->mode;
  6431. if (x < env->left && env == right_buffer) {
  6432. use_left_buffer();
  6433. } else if (x > env->width && env == left_buffer) {
  6434. use_right_buffer();
  6435. }
  6436. env->mode = current_mode;
  6437. redraw_all();
  6438. }
  6439. if (env->left) {
  6440. x -= env->left;
  6441. }
  6442. /* Figure out y coordinate */
  6443. int line_no = y + env->offset - global_config.tabs_visible;
  6444. int col_no = -1;
  6445. if (line_no > env->line_count) {
  6446. line_no = env->line_count;
  6447. }
  6448. if (line_no != env->line_no) {
  6449. env->coffset = 0;
  6450. }
  6451. /* Account for the left hand gutter */
  6452. int num_size = num_width() + gutter_width();
  6453. int _x = num_size - (line_no == env->line_no ? env->coffset : 0);
  6454. /* Determine where the cursor is physically */
  6455. for (int i = 0; i < env->lines[line_no-1]->actual; ++i) {
  6456. char_t * c = &env->lines[line_no-1]->text[i];
  6457. _x += c->display_width;
  6458. if (_x > x-1) {
  6459. col_no = i+1;
  6460. break;
  6461. }
  6462. }
  6463. if (col_no == -1 || col_no > env->lines[line_no-1]->actual) {
  6464. col_no = env->lines[line_no-1]->actual;
  6465. }
  6466. env->line_no = line_no;
  6467. env->col_no = col_no;
  6468. set_history_break();
  6469. set_preferred_column();
  6470. redraw_statusbar();
  6471. place_cursor_actual();
  6472. }
  6473. return;
  6474. }
  6475. /**
  6476. * Handle mouse event
  6477. */
  6478. BIM_ACTION(handle_mouse, 0,
  6479. "Process mouse actions."
  6480. )(void) {
  6481. int buttons = bim_getch() - 32;
  6482. int x = bim_getch() - 32;
  6483. int y = bim_getch() - 32;
  6484. handle_common_mouse(buttons, x, y);
  6485. }
  6486. BIM_ACTION(handle_mouse_sgr, 0,
  6487. "Process SGR-style mouse actions."
  6488. )(void) {
  6489. int values[3] = {0};
  6490. char tmp[512] = {0};
  6491. char * c = tmp;
  6492. int buttons = 0;
  6493. do {
  6494. int _c = bim_getch();
  6495. if (_c == -1) {
  6496. break;
  6497. }
  6498. if (_c == 'm') {
  6499. buttons = 3;
  6500. break;
  6501. } else if (_c == 'M') {
  6502. buttons = 0;
  6503. break;
  6504. }
  6505. *c = _c;
  6506. ++c;
  6507. } while (1);
  6508. char * j = tmp;
  6509. char * last = tmp;
  6510. int i = 0;
  6511. while (*j) {
  6512. if (*j == ';') {
  6513. *j = '\0';
  6514. values[i] = atoi(last);
  6515. last = j+1;
  6516. i++;
  6517. if (i == 3) break;
  6518. }
  6519. j++;
  6520. }
  6521. if (last && i < 3) values[i] = atoi(last);
  6522. if (buttons != 3) {
  6523. buttons = values[0];
  6524. }
  6525. int x = values[1];
  6526. int y = values[2];
  6527. handle_common_mouse(buttons, x, y);
  6528. }
  6529. BIM_ACTION(insert_char, ARG_IS_INPUT | ACTION_IS_RW,
  6530. "Insert one character."
  6531. )(unsigned int c) {
  6532. if (!c) {
  6533. render_error("Inserted nil byte?");
  6534. return;
  6535. }
  6536. char_t _c;
  6537. _c.codepoint = c;
  6538. _c.flags = 0;
  6539. _c.display_width = codepoint_width(c);
  6540. line_t * line = env->lines[env->line_no - 1];
  6541. line_t * nline = line_insert(line, _c, env->col_no - 1, env->line_no - 1);
  6542. if (line != nline) {
  6543. env->lines[env->line_no - 1] = nline;
  6544. }
  6545. env->col_no += 1;
  6546. set_preferred_column();
  6547. set_modified();
  6548. }
  6549. BIM_ACTION(replace_char, ARG_IS_PROMPT | ACTION_IS_RW,
  6550. "Replace a single character."
  6551. )(unsigned int c) {
  6552. if (env->col_no < 1 || env->col_no > env->lines[env->line_no-1]->actual) return;
  6553. if (c >= KEY_ESCAPE) {
  6554. render_error("Invalid key for replacement");
  6555. return;
  6556. }
  6557. char_t _c;
  6558. _c.codepoint = c;
  6559. _c.flags = 0;
  6560. _c.display_width = codepoint_width(c);
  6561. line_replace(env->lines[env->line_no-1], _c, env->col_no-1, env->line_no-1);
  6562. redraw_line(env->line_no-1);
  6563. set_modified();
  6564. }
  6565. BIM_ACTION(undo_history, ACTION_IS_RW,
  6566. "Undo history until the last breakpoint."
  6567. )(void) {
  6568. if (!global_config.history_enabled) return;
  6569. env->loading = 1;
  6570. history_t * e = env->history;
  6571. if (e->type == HISTORY_SENTINEL) {
  6572. env->loading = 0;
  6573. render_commandline_message("Already at oldest change");
  6574. return;
  6575. }
  6576. int count_chars = 0;
  6577. int count_lines = 0;
  6578. do {
  6579. if (e->type == HISTORY_SENTINEL) break;
  6580. switch (e->type) {
  6581. case HISTORY_INSERT:
  6582. /* Delete */
  6583. line_delete(
  6584. env->lines[e->contents.insert_delete_replace.lineno],
  6585. e->contents.insert_delete_replace.offset+1,
  6586. e->contents.insert_delete_replace.lineno
  6587. );
  6588. count_chars++;
  6589. break;
  6590. case HISTORY_DELETE:
  6591. {
  6592. char_t _c = {codepoint_width(e->contents.insert_delete_replace.old_codepoint),0,e->contents.insert_delete_replace.old_codepoint};
  6593. env->lines[e->contents.insert_delete_replace.lineno] = line_insert(
  6594. env->lines[e->contents.insert_delete_replace.lineno],
  6595. _c,
  6596. e->contents.insert_delete_replace.offset-1,
  6597. e->contents.insert_delete_replace.lineno
  6598. );
  6599. }
  6600. count_chars++;
  6601. break;
  6602. case HISTORY_REPLACE:
  6603. {
  6604. char_t _o = {codepoint_width(e->contents.insert_delete_replace.old_codepoint),0,e->contents.insert_delete_replace.old_codepoint};
  6605. line_replace(
  6606. env->lines[e->contents.insert_delete_replace.lineno],
  6607. _o,
  6608. e->contents.insert_delete_replace.offset,
  6609. e->contents.insert_delete_replace.lineno
  6610. );
  6611. }
  6612. count_chars++;
  6613. break;
  6614. case HISTORY_REMOVE_LINE:
  6615. env->lines = add_line(env->lines, e->contents.remove_replace_line.lineno);
  6616. replace_line(env->lines, e->contents.remove_replace_line.lineno, e->contents.remove_replace_line.old_contents);
  6617. count_lines++;
  6618. break;
  6619. case HISTORY_ADD_LINE:
  6620. env->lines = remove_line(env->lines, e->contents.add_merge_split_lines.lineno);
  6621. count_lines++;
  6622. break;
  6623. case HISTORY_REPLACE_LINE:
  6624. replace_line(env->lines, e->contents.remove_replace_line.lineno, e->contents.remove_replace_line.old_contents);
  6625. count_lines++;
  6626. break;
  6627. case HISTORY_SPLIT_LINE:
  6628. env->lines = merge_lines(env->lines, e->contents.add_merge_split_lines.lineno+1);
  6629. count_lines++;
  6630. break;
  6631. case HISTORY_MERGE_LINES:
  6632. env->lines = split_line(env->lines, e->contents.add_merge_split_lines.lineno-1, e->contents.add_merge_split_lines.split);
  6633. count_lines++;
  6634. break;
  6635. case HISTORY_BREAK:
  6636. /* Ignore break */
  6637. break;
  6638. default:
  6639. render_error("Unknown type %d!\n", e->type);
  6640. break;
  6641. }
  6642. env->line_no = env->history->line;
  6643. env->col_no = env->history->col;
  6644. env->history = e->previous;
  6645. e = env->history;
  6646. } while (e->type != HISTORY_BREAK);
  6647. if (env->line_no > env->line_count) env->line_no = env->line_count;
  6648. if (env->line_no < 1) env->line_no = 1;
  6649. if (env->col_no > env->lines[env->line_no-1]->actual) env->col_no = env->lines[env->line_no-1]->actual;
  6650. if (env->col_no < 1) env->col_no = 1;
  6651. env->modified = (env->history != env->last_save_history);
  6652. env->loading = 0;
  6653. for (int i = 0; i < env->line_count; ++i) {
  6654. env->lines[i]->istate = 0;
  6655. recalculate_tabs(env->lines[i]);
  6656. }
  6657. for (int i = 0; i < env->line_count; ++i) {
  6658. recalculate_syntax(env->lines[i],i);
  6659. }
  6660. place_cursor_actual();
  6661. update_title();
  6662. redraw_all();
  6663. render_commandline_message("%d character%s, %d line%s changed",
  6664. count_chars, (count_chars == 1) ? "" : "s",
  6665. count_lines, (count_lines == 1) ? "" : "s");
  6666. }
  6667. BIM_ACTION(redo_history, ACTION_IS_RW,
  6668. "Redo history until the next breakpoint."
  6669. )(void) {
  6670. if (!global_config.history_enabled) return;
  6671. env->loading = 1;
  6672. history_t * e = env->history->next;
  6673. if (!e) {
  6674. env->loading = 0;
  6675. render_commandline_message("Already at newest change");
  6676. return;
  6677. }
  6678. int count_chars = 0;
  6679. int count_lines = 0;
  6680. while (e) {
  6681. if (e->type == HISTORY_BREAK) {
  6682. env->history = e;
  6683. break;
  6684. }
  6685. switch (e->type) {
  6686. case HISTORY_INSERT:
  6687. {
  6688. char_t _c = {codepoint_width(e->contents.insert_delete_replace.codepoint),0,e->contents.insert_delete_replace.codepoint};
  6689. env->lines[e->contents.insert_delete_replace.lineno] = line_insert(
  6690. env->lines[e->contents.insert_delete_replace.lineno],
  6691. _c,
  6692. e->contents.insert_delete_replace.offset,
  6693. e->contents.insert_delete_replace.lineno
  6694. );
  6695. }
  6696. count_chars++;
  6697. break;
  6698. case HISTORY_DELETE:
  6699. /* Delete */
  6700. line_delete(
  6701. env->lines[e->contents.insert_delete_replace.lineno],
  6702. e->contents.insert_delete_replace.offset,
  6703. e->contents.insert_delete_replace.lineno
  6704. );
  6705. count_chars++;
  6706. break;
  6707. case HISTORY_REPLACE:
  6708. {
  6709. char_t _o = {codepoint_width(e->contents.insert_delete_replace.codepoint),0,e->contents.insert_delete_replace.codepoint};
  6710. line_replace(
  6711. env->lines[e->contents.insert_delete_replace.lineno],
  6712. _o,
  6713. e->contents.insert_delete_replace.offset,
  6714. e->contents.insert_delete_replace.lineno
  6715. );
  6716. }
  6717. count_chars++;
  6718. break;
  6719. case HISTORY_ADD_LINE:
  6720. env->lines = add_line(env->lines, e->contents.remove_replace_line.lineno);
  6721. count_lines++;
  6722. break;
  6723. case HISTORY_REMOVE_LINE:
  6724. env->lines = remove_line(env->lines, e->contents.remove_replace_line.lineno);
  6725. count_lines++;
  6726. break;
  6727. case HISTORY_REPLACE_LINE:
  6728. replace_line(env->lines, e->contents.remove_replace_line.lineno, e->contents.remove_replace_line.contents);
  6729. count_lines++;
  6730. break;
  6731. case HISTORY_MERGE_LINES:
  6732. env->lines = merge_lines(env->lines, e->contents.add_merge_split_lines.lineno);
  6733. count_lines++;
  6734. break;
  6735. case HISTORY_SPLIT_LINE:
  6736. env->lines = split_line(env->lines, e->contents.add_merge_split_lines.lineno, e->contents.add_merge_split_lines.split);
  6737. count_lines++;
  6738. break;
  6739. case HISTORY_BREAK:
  6740. /* Ignore break */
  6741. break;
  6742. default:
  6743. render_error("Unknown type %d!\n", e->type);
  6744. break;
  6745. }
  6746. env->history = e;
  6747. e = e->next;
  6748. }
  6749. env->line_no = env->history->line;
  6750. env->col_no = env->history->col;
  6751. if (env->line_no > env->line_count) env->line_no = env->line_count;
  6752. if (env->line_no < 1) env->line_no = 1;
  6753. if (env->col_no > env->lines[env->line_no-1]->actual) env->col_no = env->lines[env->line_no-1]->actual;
  6754. if (env->col_no < 1) env->col_no = 1;
  6755. env->modified = (env->history != env->last_save_history);
  6756. env->loading = 0;
  6757. for (int i = 0; i < env->line_count; ++i) {
  6758. env->lines[i]->istate = 0;
  6759. recalculate_tabs(env->lines[i]);
  6760. }
  6761. for (int i = 0; i < env->line_count; ++i) {
  6762. recalculate_syntax(env->lines[i],i);
  6763. }
  6764. place_cursor_actual();
  6765. update_title();
  6766. redraw_all();
  6767. render_commandline_message("%d character%s, %d line%s changed",
  6768. count_chars, (count_chars == 1) ? "" : "s",
  6769. count_lines, (count_lines == 1) ? "" : "s");
  6770. }
  6771. int is_whitespace(int codepoint) {
  6772. return codepoint == ' ' || codepoint == '\t';
  6773. }
  6774. int is_normal(int codepoint) {
  6775. return isalnum(codepoint) || codepoint == '_';
  6776. }
  6777. int is_special(int codepoint) {
  6778. return !is_normal(codepoint) && !is_whitespace(codepoint);
  6779. }
  6780. BIM_ACTION(word_left, 0,
  6781. "Move the cursor left to the previous word."
  6782. )(void) {
  6783. if (!env->lines[env->line_no-1]) return;
  6784. while (env->col_no > 1 && is_whitespace(env->lines[env->line_no - 1]->text[env->col_no - 2].codepoint)) {
  6785. env->col_no -= 1;
  6786. }
  6787. if (env->col_no == 1) {
  6788. if (env->line_no == 1) goto _place;
  6789. env->line_no--;
  6790. env->col_no = env->lines[env->line_no-1]->actual;
  6791. goto _place;
  6792. }
  6793. int (*inverse_comparator)(int) = is_special;
  6794. if (env->col_no > 1 && is_special(env->lines[env->line_no - 1]->text[env->col_no - 2].codepoint)) {
  6795. inverse_comparator = is_normal;
  6796. }
  6797. do {
  6798. if (env->col_no > 1) {
  6799. env->col_no -= 1;
  6800. }
  6801. } while (env->col_no > 1 && !is_whitespace(env->lines[env->line_no - 1]->text[env->col_no - 2].codepoint) && !inverse_comparator(env->lines[env->line_no - 1]->text[env->col_no - 2].codepoint));
  6802. _place:
  6803. set_preferred_column();
  6804. place_cursor_actual();
  6805. }
  6806. BIM_ACTION(big_word_left, 0,
  6807. "Move the cursor left to the previous WORD."
  6808. )(void) {
  6809. int line_no = env->line_no;
  6810. int col_no = env->col_no;
  6811. do {
  6812. col_no--;
  6813. while (col_no == 0) {
  6814. line_no--;
  6815. if (line_no == 0) {
  6816. goto_line(1);
  6817. set_preferred_column();
  6818. return;
  6819. }
  6820. col_no = env->lines[line_no-1]->actual;
  6821. }
  6822. } while (isspace(env->lines[line_no-1]->text[col_no-1].codepoint));
  6823. do {
  6824. col_no--;
  6825. if (col_no == 0) {
  6826. env->col_no = 1;
  6827. env->line_no = line_no;
  6828. set_preferred_column();
  6829. redraw_statusbar();
  6830. place_cursor_actual();
  6831. return;
  6832. }
  6833. } while (!isspace(env->lines[line_no-1]->text[col_no-1].codepoint));
  6834. env->col_no = col_no;
  6835. env->line_no = line_no;
  6836. set_preferred_column();
  6837. cursor_right();
  6838. }
  6839. BIM_ACTION(word_right, 0,
  6840. "Move the cursor right to the start of the next word."
  6841. )(void) {
  6842. if (!env->lines[env->line_no-1]) return;
  6843. if (env->col_no >= env->lines[env->line_no-1]->actual) {
  6844. /* next line */
  6845. if (env->line_no == env->line_count) return;
  6846. env->line_no++;
  6847. env->col_no = 0;
  6848. if (env->col_no >= env->lines[env->line_no-1]->actual) {
  6849. goto _place;
  6850. }
  6851. }
  6852. if (env->col_no < env->lines[env->line_no-1]->actual && is_whitespace(env->lines[env->line_no-1]->text[env->col_no - 1].codepoint)) {
  6853. while (env->col_no < env->lines[env->line_no-1]->actual && is_whitespace(env->lines[env->line_no-1]->text[env->col_no - 1].codepoint)) {
  6854. env->col_no++;
  6855. }
  6856. goto _place;
  6857. }
  6858. int (*inverse_comparator)(int) = is_special;
  6859. if (is_special(env->lines[env->line_no - 1]->text[env->col_no - 1].codepoint)) {
  6860. inverse_comparator = is_normal;
  6861. }
  6862. while (env->col_no < env->lines[env->line_no-1]->actual && !is_whitespace(env->lines[env->line_no - 1]->text[env->col_no - 1].codepoint) && !inverse_comparator(env->lines[env->line_no - 1]->text[env->col_no - 1].codepoint)) {
  6863. env->col_no++;
  6864. }
  6865. while (env->col_no < env->lines[env->line_no-1]->actual && is_whitespace(env->lines[env->line_no - 1]->text[env->col_no - 1].codepoint)) {
  6866. env->col_no++;
  6867. }
  6868. _place:
  6869. set_preferred_column();
  6870. place_cursor_actual();
  6871. }
  6872. BIM_ACTION(big_word_right, 0,
  6873. "Move the cursor right to the start of the next WORD."
  6874. )(void) {
  6875. int line_no = env->line_no;
  6876. int col_no = env->col_no;
  6877. do {
  6878. col_no++;
  6879. if (col_no > env->lines[line_no-1]->actual) {
  6880. line_no++;
  6881. if (line_no > env->line_count) {
  6882. env->line_no = env->line_count;
  6883. env->col_no = env->lines[env->line_no-1]->actual;
  6884. set_preferred_column();
  6885. redraw_statusbar();
  6886. place_cursor_actual();
  6887. return;
  6888. }
  6889. col_no = 0;
  6890. break;
  6891. }
  6892. } while (!isspace(env->lines[line_no-1]->text[col_no-1].codepoint));
  6893. do {
  6894. col_no++;
  6895. while (col_no > env->lines[line_no-1]->actual) {
  6896. line_no++;
  6897. if (line_no >= env->line_count) {
  6898. env->col_no = env->lines[env->line_count-1]->actual;
  6899. env->line_no = env->line_count;
  6900. set_preferred_column();
  6901. redraw_statusbar();
  6902. place_cursor_actual();
  6903. return;
  6904. }
  6905. col_no = 1;
  6906. }
  6907. } while (isspace(env->lines[line_no-1]->text[col_no-1].codepoint));
  6908. env->col_no = col_no;
  6909. env->line_no = line_no;
  6910. set_preferred_column();
  6911. redraw_statusbar();
  6912. place_cursor_actual();
  6913. return;
  6914. }
  6915. BIM_ACTION(delete_at_cursor, ACTION_IS_RW,
  6916. "Delete the character at the cursor, or merge with previous line."
  6917. )(void) {
  6918. if (env->col_no > 1) {
  6919. line_delete(env->lines[env->line_no - 1], env->col_no - 1, env->line_no - 1);
  6920. env->col_no -= 1;
  6921. if (env->coffset > 0) env->coffset--;
  6922. redraw_line(env->line_no-1);
  6923. set_modified();
  6924. redraw_statusbar();
  6925. place_cursor_actual();
  6926. } else if (env->line_no > 1) {
  6927. int tmp = env->lines[env->line_no - 2]->actual;
  6928. merge_lines(env->lines, env->line_no - 1);
  6929. env->line_no -= 1;
  6930. env->col_no = tmp+1;
  6931. set_preferred_column();
  6932. redraw_text();
  6933. set_modified();
  6934. redraw_statusbar();
  6935. place_cursor_actual();
  6936. }
  6937. }
  6938. BIM_ACTION(delete_word, ACTION_IS_RW,
  6939. "Delete the previous word."
  6940. )(void) {
  6941. if (!env->lines[env->line_no-1]) return;
  6942. if (env->col_no > 1) {
  6943. /* Start by deleting whitespace */
  6944. while (env->col_no > 1 && is_whitespace(env->lines[env->line_no - 1]->text[env->col_no - 2].codepoint)) {
  6945. line_delete(env->lines[env->line_no - 1], env->col_no - 1, env->line_no - 1);
  6946. env->col_no -= 1;
  6947. if (env->coffset > 0) env->coffset--;
  6948. }
  6949. int (*inverse_comparator)(int) = is_special;
  6950. if (env->col_no > 1 && is_special(env->lines[env->line_no - 1]->text[env->col_no - 2].codepoint)) {
  6951. inverse_comparator = is_normal;
  6952. }
  6953. do {
  6954. if (env->col_no > 1) {
  6955. line_delete(env->lines[env->line_no - 1], env->col_no - 1, env->line_no - 1);
  6956. env->col_no -= 1;
  6957. if (env->coffset > 0) env->coffset--;
  6958. }
  6959. } while (env->col_no > 1 && !is_whitespace(env->lines[env->line_no - 1]->text[env->col_no - 2].codepoint) && !inverse_comparator(env->lines[env->line_no - 1]->text[env->col_no - 2].codepoint));
  6960. set_preferred_column();
  6961. redraw_text();
  6962. set_modified();
  6963. redraw_statusbar();
  6964. place_cursor_actual();
  6965. }
  6966. }
  6967. BIM_ACTION(insert_line_feed, ACTION_IS_RW,
  6968. "Insert a line break, splitting the current line into two."
  6969. )(void) {
  6970. if (env->indent) {
  6971. if ((env->lines[env->line_no-1]->text[env->col_no-2].flags & 0x1F) == FLAG_COMMENT &&
  6972. (env->lines[env->line_no-1]->text[env->col_no-2].codepoint == ' ') &&
  6973. (env->col_no > 3) &&
  6974. (env->lines[env->line_no-1]->text[env->col_no-3].codepoint == '*')) {
  6975. delete_at_cursor();
  6976. }
  6977. }
  6978. if (env->col_no == env->lines[env->line_no - 1]->actual + 1) {
  6979. env->lines = add_line(env->lines, env->line_no);
  6980. } else {
  6981. env->lines = split_line(env->lines, env->line_no-1, env->col_no - 1);
  6982. }
  6983. env->coffset = 0;
  6984. env->col_no = 1;
  6985. env->line_no += 1;
  6986. set_preferred_column();
  6987. add_indent(env->line_no-1,env->line_no-2,0);
  6988. if (env->line_no > env->offset + global_config.term_height - global_config.bottom_size - 1) {
  6989. env->offset += 1;
  6990. }
  6991. if (env->highlighting_paren && env->highlighting_paren > env->line_no) env->highlighting_paren++;
  6992. set_modified();
  6993. }
  6994. BIM_ACTION(yank_lines, 0,
  6995. "Copy lines into the paste buffer."
  6996. )(void) {
  6997. int start = env->start_line;
  6998. int end = env->line_no;
  6999. if (global_config.yanks) {
  7000. for (unsigned int i = 0; i < global_config.yank_count; ++i) {
  7001. free(global_config.yanks[i]);
  7002. }
  7003. free(global_config.yanks);
  7004. }
  7005. int lines_to_yank;
  7006. int start_point;
  7007. if (start <= end) {
  7008. lines_to_yank = end - start + 1;
  7009. start_point = start - 1;
  7010. } else {
  7011. lines_to_yank = start - end + 1;
  7012. start_point = end - 1;
  7013. }
  7014. global_config.yanks = malloc(sizeof(line_t *) * lines_to_yank);
  7015. global_config.yank_count = lines_to_yank;
  7016. global_config.yank_is_full_lines = 1;
  7017. for (int i = 0; i < lines_to_yank; ++i) {
  7018. global_config.yanks[i] = malloc(sizeof(line_t) + sizeof(char_t) * (env->lines[start_point+i]->available));
  7019. global_config.yanks[i]->available = env->lines[start_point+i]->available;
  7020. global_config.yanks[i]->actual = env->lines[start_point+i]->actual;
  7021. global_config.yanks[i]->istate = 0;
  7022. memcpy(&global_config.yanks[i]->text, &env->lines[start_point+i]->text, sizeof(char_t) * (env->lines[start_point+i]->actual));
  7023. for (int j = 0; j < global_config.yanks[i]->actual; ++j) {
  7024. global_config.yanks[i]->text[j].flags = 0;
  7025. }
  7026. }
  7027. }
  7028. /**
  7029. * Helper to yank part of a line into a new yank line.
  7030. */
  7031. void yank_partial_line(int yank_no, int line_no, int start_off, int count) {
  7032. global_config.yanks[yank_no] = malloc(sizeof(line_t) + sizeof(char_t) * (count + 1));
  7033. global_config.yanks[yank_no]->available = count + 1; /* ensure extra space */
  7034. global_config.yanks[yank_no]->actual = count;
  7035. global_config.yanks[yank_no]->istate = 0;
  7036. memcpy(&global_config.yanks[yank_no]->text, &env->lines[line_no]->text[start_off], sizeof(char_t) * count);
  7037. for (int i = 0; i < count; ++i) {
  7038. global_config.yanks[yank_no]->text[i].flags = 0;
  7039. }
  7040. }
  7041. /**
  7042. * Yank text...
  7043. */
  7044. void yank_text(int start_line, int start_col, int end_line, int end_col) {
  7045. if (global_config.yanks) {
  7046. for (unsigned int i = 0; i < global_config.yank_count; ++i) {
  7047. free(global_config.yanks[i]);
  7048. }
  7049. free(global_config.yanks);
  7050. }
  7051. int lines_to_yank = end_line - start_line + 1;
  7052. int start_point = start_line - 1;
  7053. global_config.yanks = malloc(sizeof(line_t *) * lines_to_yank);
  7054. global_config.yank_count = lines_to_yank;
  7055. global_config.yank_is_full_lines = 0;
  7056. if (lines_to_yank == 1) {
  7057. yank_partial_line(0, start_point, start_col - 1, (end_col - start_col + 1));
  7058. } else {
  7059. yank_partial_line(0, start_point, start_col - 1, (env->lines[start_point]->actual - start_col + 1));
  7060. /* Yank middle lines */
  7061. for (int i = 1; i < lines_to_yank - 1; ++i) {
  7062. global_config.yanks[i] = malloc(sizeof(line_t) + sizeof(char_t) * (env->lines[start_point+i]->available));
  7063. global_config.yanks[i]->available = env->lines[start_point+i]->available;
  7064. global_config.yanks[i]->actual = env->lines[start_point+i]->actual;
  7065. global_config.yanks[i]->istate = 0;
  7066. memcpy(&global_config.yanks[i]->text, &env->lines[start_point+i]->text, sizeof(char_t) * (env->lines[start_point+i]->actual));
  7067. for (int j = 0; j < global_config.yanks[i]->actual; ++j) {
  7068. global_config.yanks[i]->text[j].flags = 0;
  7069. }
  7070. }
  7071. /* Yank end line */
  7072. yank_partial_line(lines_to_yank-1, end_line - 1, 0, end_col);
  7073. }
  7074. }
  7075. BIM_ACTION(delete_at_column, ARG_IS_CUSTOM | ACTION_IS_RW,
  7076. "Delete from the current column backwards (`<backspace>`) or forwards (`<del>`)."
  7077. )(int direction) {
  7078. if (direction == -1 && env->sel_col <= 0) return;
  7079. int prev_width = 0;
  7080. for (int i = env->line_no; i <= env->start_line; i++) {
  7081. line_t * line = env->lines[i - 1];
  7082. int _x = 0;
  7083. int col = 1;
  7084. int j = 0;
  7085. for (; j < line->actual; ++j) {
  7086. char_t * c = &line->text[j];
  7087. _x += c->display_width;
  7088. col = j+1;
  7089. if (_x > env->sel_col) break;
  7090. prev_width = c->display_width;
  7091. }
  7092. if ((direction == -1) && (_x == env->sel_col && j == line->actual)) {
  7093. line_delete(line, line->actual, i - 1);
  7094. set_modified();
  7095. } else if (_x > env->sel_col) {
  7096. line_delete(line, col - (direction == -1 ? 1 : 0), i - 1);
  7097. set_modified();
  7098. }
  7099. }
  7100. if (direction == -1) {
  7101. env->sel_col -= prev_width;
  7102. env->col_no--;
  7103. }
  7104. redraw_text();
  7105. }
  7106. uint32_t * get_word_under_cursor(void) {
  7107. /* Figure out size */
  7108. int c_before = 0;
  7109. int c_after = 0;
  7110. int i = env->col_no;
  7111. while (i > 0) {
  7112. if (!simple_keyword_qualifier(env->lines[env->line_no-1]->text[i-1].codepoint)) break;
  7113. c_before++;
  7114. i--;
  7115. }
  7116. i = env->col_no+1;
  7117. while (i < env->lines[env->line_no-1]->actual+1) {
  7118. if (!simple_keyword_qualifier(env->lines[env->line_no-1]->text[i-1].codepoint)) break;
  7119. c_after++;
  7120. i++;
  7121. }
  7122. if (!c_before && !c_after) return NULL;
  7123. /* Populate with characters */
  7124. uint32_t * out = malloc(sizeof(uint32_t) * (c_before+c_after+1));
  7125. int j = 0;
  7126. while (c_before) {
  7127. out[j] = env->lines[env->line_no-1]->text[env->col_no-c_before].codepoint;
  7128. c_before--;
  7129. j++;
  7130. }
  7131. int x = 0;
  7132. while (c_after) {
  7133. out[j] = env->lines[env->line_no-1]->text[env->col_no+x].codepoint;
  7134. j++;
  7135. x++;
  7136. c_after--;
  7137. }
  7138. out[j] = 0;
  7139. return out;
  7140. }
  7141. BIM_ACTION(search_under_cursor, 0,
  7142. "Search for the word currently under the cursor."
  7143. )(void) {
  7144. if (global_config.search) free(global_config.search);
  7145. global_config.search = get_word_under_cursor();
  7146. /* Find it */
  7147. search_next();
  7148. }
  7149. BIM_ACTION(find_character_forward, ARG_IS_PROMPT | ARG_IS_INPUT,
  7150. "Find a character forward on the current line and place the cursor on (`f`) or before (`t`) it."
  7151. )(int type, int c) {
  7152. for (int i = env->col_no+1; i <= env->lines[env->line_no-1]->actual; ++i) {
  7153. if (env->lines[env->line_no-1]->text[i-1].codepoint == c) {
  7154. env->col_no = i - !!(type == 't');
  7155. place_cursor_actual();
  7156. set_preferred_column();
  7157. return;
  7158. }
  7159. }
  7160. }
  7161. BIM_ACTION(find_character_backward, ARG_IS_PROMPT | ARG_IS_INPUT,
  7162. "Find a character backward on the current line and place the cursor on (`F`) or after (`T`) it."
  7163. )(int type, int c) {
  7164. for (int i = env->col_no-1; i >= 1; --i) {
  7165. if (env->lines[env->line_no-1]->text[i-1].codepoint == c) {
  7166. env->col_no = i + !!(type == 'T');
  7167. place_cursor_actual();
  7168. set_preferred_column();
  7169. return;
  7170. }
  7171. }
  7172. }
  7173. /**
  7174. * Clear the navigation number buffer
  7175. */
  7176. void reset_nav_buffer(int c) {
  7177. if (c == KEY_TIMEOUT) return;
  7178. if (nav_buffer && (c < '0' || c > '9')) {
  7179. nav_buffer = 0;
  7180. redraw_commandline();
  7181. }
  7182. }
  7183. /**
  7184. * Determine if a column + line number are within range of the
  7185. * current character selection specified by start_line, etc.
  7186. *
  7187. * Used to determine how syntax flags should be set when redrawing
  7188. * selected text in CHAR SELECTION mode.
  7189. */
  7190. int point_in_range(int start_line, int end_line, int start_col, int end_col, int line, int col) {
  7191. if (start_line == end_line) {
  7192. if ( end_col < start_col) {
  7193. int tmp = end_col;
  7194. end_col = start_col;
  7195. start_col = tmp;
  7196. }
  7197. return (col >= start_col && col <= end_col);
  7198. }
  7199. if (start_line > end_line) {
  7200. int tmp = end_line;
  7201. end_line = start_line;
  7202. start_line = tmp;
  7203. tmp = end_col;
  7204. end_col = start_col;
  7205. start_col = tmp;
  7206. }
  7207. if (line < start_line || line > end_line) return 0;
  7208. if (line == start_line) {
  7209. return col >= start_col;
  7210. }
  7211. if (line == end_line) {
  7212. return col <= end_col;
  7213. }
  7214. return 1;
  7215. }
  7216. /**
  7217. * Macro for redrawing selected lines with appropriate highlighting.
  7218. */
  7219. #define _redraw_line(line, force_start_line) \
  7220. do { \
  7221. if (!(force_start_line) && (line) == env->start_line) break; \
  7222. if ((line) > env->line_count + 1) { \
  7223. if ((line) - env->offset - 1 < global_config.term_height - global_config.bottom_size - 1) { \
  7224. draw_excess_line((line) - env->offset - 1); \
  7225. } \
  7226. break; \
  7227. } \
  7228. if ((env->line_no < env->start_line && ((line) < env->line_no || (line) > env->start_line)) || \
  7229. (env->line_no > env->start_line && ((line) > env->line_no || (line) < env->start_line)) || \
  7230. (env->line_no == env->start_line && (line) != env->start_line)) { \
  7231. recalculate_syntax(env->lines[(line)-1],(line)-1); \
  7232. } else { \
  7233. for (int j = 0; j < env->lines[(line)-1]->actual; ++j) { \
  7234. env->lines[(line)-1]->text[j].flags |= FLAG_SELECT; \
  7235. } \
  7236. } \
  7237. redraw_line((line)-1); \
  7238. } while (0)
  7239. #define _redraw_line_char(line, force_start_line) \
  7240. do { \
  7241. if (!(force_start_line) && (line) == env->start_line) break; \
  7242. if ((line) > env->line_count + 1) { \
  7243. if ((line) - env->offset - 1 < global_config.term_height - global_config.bottom_size - 1) { \
  7244. draw_excess_line((line) - env->offset - 1); \
  7245. } \
  7246. break; \
  7247. } \
  7248. if ((env->line_no < env->start_line && ((line) < env->line_no || (line) > env->start_line)) || \
  7249. (env->line_no > env->start_line && ((line) > env->line_no || (line) < env->start_line)) || \
  7250. (env->line_no == env->start_line && (line) != env->start_line)) { \
  7251. /* Line is completely outside selection */ \
  7252. recalculate_syntax(env->lines[(line)-1],(line)-1); \
  7253. } else { \
  7254. if ((line) == env->start_line || (line) == env->line_no) { \
  7255. recalculate_syntax(env->lines[(line)-1],(line)-1); \
  7256. } \
  7257. for (int j = 0; j < env->lines[(line)-1]->actual; ++j) { \
  7258. if (point_in_range(env->start_line, env->line_no,env->start_col, env->col_no, (line), j+1)) { \
  7259. env->lines[(line)-1]->text[j].flags |= FLAG_SELECT; \
  7260. } \
  7261. } \
  7262. } \
  7263. redraw_line((line)-1); \
  7264. } while (0)
  7265. #define _redraw_line_col(line, force_start_line) \
  7266. do {\
  7267. if (!(force_start_line) && (line) == env->start_line) break; \
  7268. if ((line) > env->line_count + 1) { \
  7269. if ((line) - env->offset - 1 < global_config.term_height - global_config.bottom_size - 1) { \
  7270. draw_excess_line((line) - env->offset - 1); \
  7271. } \
  7272. break; \
  7273. } \
  7274. redraw_line((line)-1); \
  7275. } while (0)
  7276. BIM_ACTION(adjust_indent, ARG_IS_CUSTOM | ACTION_IS_RW,
  7277. "Adjust the indentation on the selected lines (`<tab>` for deeper, `<shift-tab>` for shallower)."
  7278. )(int direction) {
  7279. int lines_to_cover = 0;
  7280. int start_point = 0;
  7281. if (env->start_line <= env->line_no) {
  7282. start_point = env->start_line - 1;
  7283. lines_to_cover = env->line_no - env->start_line + 1;
  7284. } else {
  7285. start_point = env->line_no - 1;
  7286. lines_to_cover = env->start_line - env->line_no + 1;
  7287. }
  7288. for (int i = 0; i < lines_to_cover; ++i) {
  7289. if (env->lines[start_point + i]->actual < 1) continue;
  7290. if (direction == -1) {
  7291. if (env->tabs) {
  7292. if (env->lines[start_point + i]->text[0].codepoint == '\t') {
  7293. line_delete(env->lines[start_point + i],1,start_point+i);
  7294. _redraw_line(start_point+i+1,1);
  7295. }
  7296. } else {
  7297. for (int j = 0; j < env->tabstop; ++j) {
  7298. if (env->lines[start_point + i]->text[0].codepoint == ' ') {
  7299. line_delete(env->lines[start_point + i],1,start_point+i);
  7300. }
  7301. }
  7302. _redraw_line(start_point+i+1,1);
  7303. }
  7304. } else if (direction == 1) {
  7305. if (env->tabs) {
  7306. char_t c;
  7307. c.codepoint = '\t';
  7308. c.display_width = env->tabstop;
  7309. c.flags = FLAG_SELECT;
  7310. env->lines[start_point + i] = line_insert(env->lines[start_point + i], c, 0, start_point + i);
  7311. } else {
  7312. for (int j = 0; j < env->tabstop; ++j) {
  7313. char_t c;
  7314. c.codepoint = ' ';
  7315. c.display_width = 1;
  7316. c.flags = FLAG_SELECT;
  7317. env->lines[start_point + i] = line_insert(env->lines[start_point + i], c, 0, start_point + i);
  7318. }
  7319. }
  7320. _redraw_line(start_point+i+1,1);
  7321. }
  7322. }
  7323. if (env->col_no > env->lines[env->line_no-1]->actual) {
  7324. env->col_no = env->lines[env->line_no-1]->actual;
  7325. }
  7326. set_preferred_column();
  7327. set_modified();
  7328. }
  7329. void recalculate_selected_lines(void) {
  7330. int start = env->line_no < env->start_line ? env->line_no : env->start_line;
  7331. int end = env->line_no > env->start_line ? env->line_no : env->start_line;
  7332. if (start < 1) start = 1;
  7333. if (start > env->line_count) start = env->line_count;
  7334. if (end < 1) end = 1;
  7335. if (end > env->line_count) end = env->line_count;
  7336. for (int i = (start > 1) ? (start-1) : (start); i <= end; ++i) {
  7337. recalculate_syntax(env->lines[i-1],i-1);
  7338. }
  7339. redraw_all();
  7340. }
  7341. BIM_ACTION(enter_line_selection, 0,
  7342. "Enter line selection mode."
  7343. )(void) {
  7344. /* Set mode */
  7345. env->mode = MODE_LINE_SELECTION;
  7346. /* Store start position */
  7347. env->start_line = env->line_no;
  7348. env->prev_line = env->start_line;
  7349. env->start_col = env->col_no;
  7350. /* Redraw commandline to get -- LINE SELECTION -- text */
  7351. redraw_commandline();
  7352. unhighlight_matching_paren();
  7353. /* Set this line as selected for syntax highlighting */
  7354. for (int j = 0; j < env->lines[env->line_no-1]->actual; ++j) {
  7355. env->lines[env->line_no-1]->text[j].flags |= FLAG_SELECT;
  7356. }
  7357. /* And redraw it */
  7358. redraw_line(env->line_no-1);
  7359. }
  7360. BIM_ACTION(switch_selection_mode, ARG_IS_CUSTOM,
  7361. "Swap between LINE and CHAR selection modes."
  7362. )(int mode) {
  7363. env->mode = mode;
  7364. if (mode == MODE_LINE_SELECTION) {
  7365. int start = env->line_no < env->start_line ? env->line_no : env->start_line;
  7366. int end = env->line_no > env->start_line ? env->line_no : env->start_line;
  7367. for (int i = start; i <= end; ++i) {
  7368. _redraw_line(i, 1);
  7369. }
  7370. } else if (mode == MODE_CHAR_SELECTION) {
  7371. int start = env->line_no < env->start_line ? env->line_no : env->start_line;
  7372. int end = env->line_no > env->start_line ? env->line_no : env->start_line;
  7373. for (int i = start; i <= end; ++i) {
  7374. _redraw_line_char(i, 1);
  7375. }
  7376. }
  7377. }
  7378. BIM_ACTION(delete_and_yank_lines, 0,
  7379. "Delete and yank the selected lines."
  7380. )(void) {
  7381. yank_lines();
  7382. if (env->start_line <= env->line_no) {
  7383. int lines_to_delete = env->line_no - env->start_line + 1;
  7384. for (int i = 0; i < lines_to_delete; ++i) {
  7385. env->lines = remove_line(env->lines, env->start_line-1);
  7386. }
  7387. env->line_no = env->start_line;
  7388. } else {
  7389. int lines_to_delete = env->start_line - env->line_no + 1;
  7390. for (int i = 0; i < lines_to_delete; ++i) {
  7391. env->lines = remove_line(env->lines, env->line_no-1);
  7392. }
  7393. }
  7394. if (env->line_no > env->line_count) {
  7395. env->line_no = env->line_count;
  7396. }
  7397. if (env->col_no > env->lines[env->line_no-1]->actual) {
  7398. env->col_no = env->lines[env->line_no-1]->actual;
  7399. }
  7400. set_preferred_column();
  7401. set_modified();
  7402. }
  7403. BIM_ACTION(enter_insert, ACTION_IS_RW,
  7404. "Enter insert mode."
  7405. )(void) {
  7406. env->mode = MODE_INSERT;
  7407. set_history_break();
  7408. }
  7409. BIM_ACTION(delete_lines_and_enter_insert, ACTION_IS_RW,
  7410. "Delete and yank the selected lines and then enter insert mode."
  7411. )(void) {
  7412. delete_and_yank_lines();
  7413. env->lines = add_line(env->lines, env->line_no-1);
  7414. redraw_text();
  7415. env->mode = MODE_INSERT;
  7416. }
  7417. BIM_ACTION(replace_chars_in_line, ARG_IS_PROMPT | ACTION_IS_RW,
  7418. "Replace characters in the selected lines."
  7419. )(int c) {
  7420. if (c >= KEY_ESCAPE) {
  7421. render_error("Invalid key for replacement");
  7422. return;
  7423. }
  7424. char_t _c = {codepoint_width(c), 0, c};
  7425. int start_point = env->start_line < env->line_no ? env->start_line : env->line_no;
  7426. int end_point = env->start_line < env->line_no ? env->line_no : env->start_line;
  7427. for (int line = start_point; line <= end_point; ++line) {
  7428. for (int i = 0; i < env->lines[line-1]->actual; ++i) {
  7429. line_replace(env->lines[line-1], _c, i, line-1);
  7430. }
  7431. }
  7432. }
  7433. BIM_ACTION(leave_selection, 0,
  7434. "Leave selection modes and return to normal mode."
  7435. )(void) {
  7436. set_history_break();
  7437. env->mode = MODE_NORMAL;
  7438. recalculate_selected_lines();
  7439. }
  7440. BIM_ACTION(insert_char_at_column, ARG_IS_INPUT | ACTION_IS_RW,
  7441. "Insert a character on all lines at the current column."
  7442. )(int c) {
  7443. char_t _c;
  7444. _c.codepoint = c;
  7445. _c.flags = 0;
  7446. _c.display_width = codepoint_width(c);
  7447. int inserted_width = 0;
  7448. /* For each line */
  7449. for (int i = env->line_no; i <= env->start_line; i++) {
  7450. line_t * line = env->lines[i - 1];
  7451. int _x = 0;
  7452. int col = 1;
  7453. int j = 0;
  7454. for (; j < line->actual; ++j) {
  7455. char_t * c = &line->text[j];
  7456. _x += c->display_width;
  7457. col = j+1;
  7458. if (_x > env->sel_col) break;
  7459. }
  7460. if ((_x == env->sel_col && j == line->actual)) {
  7461. _x = env->sel_col + 1;
  7462. col = line->actual + 1;
  7463. }
  7464. if (_x > env->sel_col) {
  7465. line_t * nline = line_insert(line, _c, col - 1, i - 1);
  7466. if (line != nline) {
  7467. env->lines[i - 1] = nline;
  7468. line = nline;
  7469. }
  7470. set_modified();
  7471. }
  7472. recalculate_tabs(line);
  7473. inserted_width = line->text[col-1].display_width;
  7474. }
  7475. env->sel_col += inserted_width;
  7476. env->col_no++;
  7477. }
  7478. BIM_ACTION(enter_col_insert, ACTION_IS_RW,
  7479. "Enter column insert mode."
  7480. )(void) {
  7481. if (env->start_line < env->line_no) {
  7482. /* swap */
  7483. int tmp = env->line_no;
  7484. env->line_no = env->start_line;
  7485. env->start_line = tmp;
  7486. }
  7487. env->mode = MODE_COL_INSERT;
  7488. }
  7489. BIM_ACTION(enter_col_insert_after, ACTION_IS_RW,
  7490. "Enter column insert mode after the selected column."
  7491. )(void) {
  7492. env->sel_col += 1;
  7493. enter_col_insert();
  7494. }
  7495. BIM_ACTION(delete_column, ACTION_IS_RW,
  7496. "(temporary) Delete the selected column."
  7497. )(void) {
  7498. /* TODO maybe a flag to do this so we can just call delete_at_column with arg = 1? */
  7499. if (env->start_line < env->line_no) {
  7500. int tmp = env->line_no;
  7501. env->line_no = env->start_line;
  7502. env->start_line = tmp;
  7503. }
  7504. delete_at_column(1);
  7505. }
  7506. BIM_ACTION(enter_col_selection, 0,
  7507. "Enter column selection mode."
  7508. )(void) {
  7509. /* Set mode */
  7510. env->mode = MODE_COL_SELECTION;
  7511. /* Store cursor */
  7512. env->start_line = env->line_no;
  7513. env->sel_col = env->preferred_column;
  7514. env->prev_line = env->start_line;
  7515. /* Redraw commandline */
  7516. redraw_commandline();
  7517. /* Nothing else to do here; rely on cursor */
  7518. }
  7519. BIM_ACTION(yank_characters, 0,
  7520. "Yank the selected characters to the paste buffer."
  7521. )(void) {
  7522. int end_line = env->line_no;
  7523. int end_col = env->col_no;
  7524. if (env->start_line == end_line) {
  7525. if (env->start_col > end_col) {
  7526. int tmp = env->start_col;
  7527. env->start_col = end_col;
  7528. end_col = tmp;
  7529. }
  7530. } else if (env->start_line > end_line) {
  7531. int tmp = env->start_line;
  7532. env->start_line = end_line;
  7533. end_line = tmp;
  7534. tmp = env->start_col;
  7535. env->start_col = end_col;
  7536. end_col = tmp;
  7537. }
  7538. yank_text(env->start_line, env->start_col, end_line, end_col);
  7539. }
  7540. BIM_ACTION(delete_and_yank_chars, ACTION_IS_RW,
  7541. "Delete and yank the selected characters."
  7542. )(void) {
  7543. int end_line = env->line_no;
  7544. int end_col = env->col_no;
  7545. if (env->start_line == end_line) {
  7546. if (env->start_col > end_col) {
  7547. int tmp = env->start_col;
  7548. env->start_col = end_col;
  7549. end_col = tmp;
  7550. }
  7551. yank_text(env->start_line, env->start_col, end_line, end_col);
  7552. for (int i = env->start_col; i <= end_col; ++i) {
  7553. line_delete(env->lines[env->start_line-1], env->start_col, env->start_line - 1);
  7554. }
  7555. env->col_no = env->start_col;
  7556. } else {
  7557. if (env->start_line > end_line) {
  7558. int tmp = env->start_line;
  7559. env->start_line = end_line;
  7560. end_line = tmp;
  7561. tmp = env->start_col;
  7562. env->start_col = end_col;
  7563. end_col = tmp;
  7564. }
  7565. /* Copy lines */
  7566. yank_text(env->start_line, env->start_col, end_line, end_col);
  7567. /* Delete lines */
  7568. for (int i = env->start_line+1; i < end_line; ++i) {
  7569. env->lines = remove_line(env->lines, env->start_line);
  7570. } /* end_line is no longer valid; should be start_line+1*/
  7571. /* Delete from env->start_col forward */
  7572. int tmp = env->lines[env->start_line-1]->actual;
  7573. for (int i = env->start_col; i <= tmp; ++i) {
  7574. line_delete(env->lines[env->start_line-1], env->start_col, env->start_line - 1);
  7575. }
  7576. for (int i = 1; i <= end_col; ++i) {
  7577. line_delete(env->lines[env->start_line], 1, env->start_line);
  7578. }
  7579. /* Merge start and end lines */
  7580. merge_lines(env->lines, env->start_line);
  7581. env->line_no = env->start_line;
  7582. env->col_no = env->start_col;
  7583. }
  7584. if (env->line_no > env->line_count) {
  7585. env->line_no = env->line_count;
  7586. }
  7587. set_preferred_column();
  7588. set_modified();
  7589. }
  7590. BIM_ACTION(delete_chars_and_enter_insert, ACTION_IS_RW,
  7591. "Delete and yank the selected characters and then enter insert mode."
  7592. )(void) {
  7593. delete_and_yank_chars();
  7594. redraw_text();
  7595. enter_insert();
  7596. }
  7597. BIM_ACTION(replace_chars, ARG_IS_PROMPT | ACTION_IS_RW,
  7598. "Replace the selected characters."
  7599. )(int c) {
  7600. if (c >= KEY_ESCAPE) {
  7601. render_error("Invalid key for replacement");
  7602. return;
  7603. }
  7604. char_t _c = {codepoint_width(c), 0, c};
  7605. /* This should probably be a function line "do_over_range" or something */
  7606. if (env->start_line == env->line_no) {
  7607. int s = (env->start_col < env->col_no) ? env->start_col : env->col_no;
  7608. int e = (env->start_col < env->col_no) ? env->col_no : env->start_col;
  7609. for (int i = s; i <= e; ++i) {
  7610. line_replace(env->lines[env->start_line-1], _c, i-1, env->start_line-1);
  7611. }
  7612. redraw_text();
  7613. } else {
  7614. if (env->start_line < env->line_no) {
  7615. for (int s = env->start_col-1; s < env->lines[env->start_line-1]->actual; ++s) {
  7616. line_replace(env->lines[env->start_line-1], _c, s, env->start_line-1);
  7617. }
  7618. for (int line = env->start_line + 1; line < env->line_no; ++line) {
  7619. for (int i = 0; i < env->lines[line-1]->actual; ++i) {
  7620. line_replace(env->lines[line-1], _c, i, line-1);
  7621. }
  7622. }
  7623. for (int s = 0; s < env->col_no; ++s) {
  7624. line_replace(env->lines[env->line_no-1], _c, s, env->line_no-1);
  7625. }
  7626. } else {
  7627. for (int s = env->col_no-1; s < env->lines[env->line_no-1]->actual; ++s) {
  7628. line_replace(env->lines[env->line_no-1], _c, s, env->line_no-1);
  7629. }
  7630. for (int line = env->line_no + 1; line < env->start_line; ++line) {
  7631. for (int i = 0; i < env->lines[line-1]->actual; ++i) {
  7632. line_replace(env->lines[line-1], _c, i, line-1);
  7633. }
  7634. }
  7635. for (int s = 0; s < env->start_col; ++s) {
  7636. line_replace(env->lines[env->start_line-1], _c, s, env->start_line-1);
  7637. }
  7638. }
  7639. }
  7640. }
  7641. BIM_ACTION(enter_char_selection, 0,
  7642. "Enter character selection mode."
  7643. )(void) {
  7644. /* Set mode */
  7645. env->mode = MODE_CHAR_SELECTION;
  7646. /* Set cursor positions */
  7647. env->start_line = env->line_no;
  7648. env->start_col = env->col_no;
  7649. env->prev_line = env->start_line;
  7650. /* Redraw commandline for -- CHAR SELECTION -- */
  7651. redraw_commandline();
  7652. unhighlight_matching_paren();
  7653. /* Select single character */
  7654. env->lines[env->line_no-1]->text[env->col_no-1].flags |= FLAG_SELECT;
  7655. redraw_line(env->line_no-1);
  7656. }
  7657. BIM_ACTION(insert_at_end_of_selection, ACTION_IS_RW,
  7658. "Move the cursor to the end of the selection and enter insert mode."
  7659. )(void) {
  7660. recalculate_selected_lines();
  7661. if (env->line_no == env->start_line) {
  7662. env->col_no = env->col_no > env->start_col ? env->col_no + 1 : env->start_col + 1;
  7663. } else if (env->line_no < env->start_line) {
  7664. env->col_no = env->start_col + 1;
  7665. env->line_no = env->start_line;
  7666. } else {
  7667. env->col_no += 1;
  7668. }
  7669. env->mode = MODE_INSERT;
  7670. }
  7671. void free_completion_match(struct completion_match * match) {
  7672. if (match->string) free(match->string);
  7673. if (match->file) free(match->file);
  7674. if (match->search) free(match->search);
  7675. }
  7676. /**
  7677. * Read ctags file to find matches for a symbol
  7678. */
  7679. int read_tags(uint32_t * comp, struct completion_match **matches, int * matches_count, int complete_match) {
  7680. int _matches_len = 4;
  7681. int *matches_len = &_matches_len;
  7682. *matches_count = 0;
  7683. *matches = malloc(sizeof(struct completion_match) * (*matches_len));
  7684. FILE * tags = fopen("tags","r");
  7685. if (tags) {
  7686. char tmp[4096]; /* max line */
  7687. while (!feof(tags) && fgets(tmp, 4096, tags)) {
  7688. if (tmp[0] == '!') continue;
  7689. int i = 0;
  7690. while (comp[i] && comp[i] == (unsigned int)tmp[i]) i++;
  7691. if (comp[i] == '\0') {
  7692. if (complete_match && tmp[i] != '\t') continue;
  7693. int j = i;
  7694. while (tmp[j] != '\t' && tmp[j] != '\n' && tmp[j] != '\0') j++;
  7695. tmp[j] = '\0'; j++;
  7696. char * file = &tmp[j];
  7697. while (tmp[j] != '\t' && tmp[j] != '\n' && tmp[j] != '\0') j++;
  7698. tmp[j] = '\0'; j++;
  7699. char * search = &tmp[j];
  7700. while (!(tmp[j] == '/' && tmp[j+1] == ';' && tmp[j+2] == '"' && tmp[j+3] == '\t') /* /normal searches/ */
  7701. && !(tmp[j] == ';' && tmp[j+1] == '"' && tmp[j+2] == '\t') /* Old ctags line number searches */
  7702. && (tmp[j] != '\n' && tmp[j] != '\0')) j++;
  7703. tmp[j] = '\0'; j++;
  7704. add_match(tmp,file,search);
  7705. }
  7706. }
  7707. fclose(tags);
  7708. }
  7709. /* TODO: Get these from syntax files with a dynamic callback */
  7710. if (env->syntax && env->syntax->completion_matcher) {
  7711. env->syntax->completion_matcher(comp,matches,matches_count,complete_match,matches_len);
  7712. }
  7713. return 0;
  7714. }
  7715. /**
  7716. * Draw an autocomplete popover with matches.
  7717. */
  7718. void draw_completion_matches(uint32_t * tmp, struct completion_match *matches, int matches_count, int index) {
  7719. int original_length = 0;
  7720. while (tmp[original_length]) original_length++;
  7721. int max_width = 0;
  7722. for (int i = 0; i < matches_count; ++i) {
  7723. /* TODO unicode width */
  7724. unsigned int my_width = strlen(matches[i].string) + (matches[i].file ? strlen(matches[i].file) + 1 : 0);
  7725. if (my_width > (unsigned int)max_width) {
  7726. max_width = my_width;
  7727. }
  7728. }
  7729. /* Figure out how much space we have to display the window */
  7730. int cursor_y = env->line_no - env->offset + global_config.tabs_visible;
  7731. int max_y = global_config.term_height - global_config.bottom_size - cursor_y;
  7732. /* Find a good place to put the box horizontally */
  7733. int num_size = num_width() + gutter_width();
  7734. int x = num_size + 1 - env->coffset;
  7735. /* Determine where the cursor is physically */
  7736. for (int i = 0; i < env->col_no - 1 - original_length; ++i) {
  7737. char_t * c = &env->lines[env->line_no-1]->text[i];
  7738. x += c->display_width;
  7739. }
  7740. int box_width = max_width;
  7741. int box_x = x;
  7742. int box_y = cursor_y + 1;
  7743. if (max_width > env->width - num_width() - gutter_width()) {
  7744. box_width = env->width - num_width() - gutter_width();
  7745. box_x = num_width() + gutter_width() + 1;
  7746. } else if (env->width - x < max_width) {
  7747. box_width = max_width;
  7748. box_x = env->width - max_width;
  7749. }
  7750. int max_count = (max_y < matches_count) ? max_y - 1 : matches_count;
  7751. for (int x = index; x < max_count+index; ++x) {
  7752. int i = x % matches_count;
  7753. place_cursor(box_x + env->left, box_y+x-index);
  7754. set_colors(COLOR_KEYWORD, COLOR_STATUS_BG);
  7755. /* TODO wide characters */
  7756. int match_width = strlen(matches[i].string);
  7757. int file_width = matches[i].file ? strlen(matches[i].file) : 0;
  7758. for (int j = 0; j < box_width; ++j) {
  7759. if (j == original_length) set_colors(i == index ? COLOR_NUMERAL : COLOR_STATUS_FG, COLOR_STATUS_BG);
  7760. if (j == match_width) set_colors(COLOR_TYPE, COLOR_STATUS_BG);
  7761. if (j < match_width) printf("%c", matches[i].string[j]);
  7762. else if (j > match_width && j - match_width - 1 < file_width) printf("%c", matches[i].file[j-match_width-1]);
  7763. else printf(" ");
  7764. }
  7765. }
  7766. if (max_count == 0) {
  7767. place_cursor(box_x + env->left, box_y);
  7768. set_colors(COLOR_STATUS_FG, COLOR_STATUS_BG);
  7769. printf(" (no matches) ");
  7770. } else if (max_count != matches_count) {
  7771. place_cursor(box_x + env->left, box_y+max_count);
  7772. set_colors(COLOR_STATUS_FG, COLOR_STATUS_BG);
  7773. printf(" (%d more) ", matches_count-max_count);
  7774. }
  7775. }
  7776. /**
  7777. * Autocomplete words (function/variable names, etc.) in input mode.
  7778. */
  7779. int omni_complete(int quit_quietly_on_none) {
  7780. int c;
  7781. int (*qualifier)(int c) = simple_keyword_qualifier;
  7782. if (env->syntax && env->syntax->completion_qualifier) {
  7783. qualifier = env->syntax->completion_qualifier;
  7784. }
  7785. /* Pull the word from before the cursor */
  7786. int c_before = 0;
  7787. int i = env->col_no-1;
  7788. while (i > 0) {
  7789. int c = env->lines[env->line_no-1]->text[i-1].codepoint;
  7790. if (!qualifier(c)) break;
  7791. c_before++;
  7792. i--;
  7793. }
  7794. if (!c_before && quit_quietly_on_none) return 0;
  7795. /* Populate with characters */
  7796. uint32_t * tmp = malloc(sizeof(uint32_t) * (c_before+1));
  7797. int j = 0;
  7798. while (c_before) {
  7799. tmp[j] = env->lines[env->line_no-1]->text[env->col_no-c_before-1].codepoint;
  7800. c_before--;
  7801. j++;
  7802. }
  7803. tmp[j] = 0;
  7804. /*
  7805. * TODO matches should probably be a struct with more data than just
  7806. * the matching string; maybe offset where the needle was found,
  7807. * class information, source file information - anything we can extract
  7808. * from ctags, but also other information for other sources of completion.
  7809. */
  7810. struct completion_match *matches;
  7811. int matches_count;
  7812. /* TODO just reading ctags is rather mediocre; can we do something cool here? */
  7813. if (read_tags(tmp, &matches, &matches_count, 0)) goto _completion_done;
  7814. /* Draw box with matches at cursor-width(tmp) */
  7815. if (quit_quietly_on_none && matches_count == 0) return 0;
  7816. draw_completion_matches(tmp, matches, matches_count, 0);
  7817. int retval = 0;
  7818. int index = 0;
  7819. _completion_done:
  7820. place_cursor_actual();
  7821. while (1) {
  7822. c = bim_getch();
  7823. if (c == -1) continue;
  7824. if (matches_count < 1) {
  7825. redraw_all();
  7826. break;
  7827. }
  7828. if (c == 15) {
  7829. index = (index + 1) % matches_count;
  7830. draw_completion_matches(tmp, matches, matches_count, index);
  7831. place_cursor_actual();
  7832. continue;
  7833. } else if (c == '\t') {
  7834. for (unsigned int i = j; i < strlen(matches[index].string); ++i) {
  7835. insert_char(matches[index].string[i]);
  7836. }
  7837. set_preferred_column();
  7838. redraw_text();
  7839. place_cursor_actual();
  7840. goto _finish_completion;
  7841. } else if (isgraph(c) && c != '}') {
  7842. /* insert and continue matching */
  7843. insert_char(c);
  7844. set_preferred_column();
  7845. redraw_text();
  7846. place_cursor_actual();
  7847. retval = 1;
  7848. goto _finish_completion;
  7849. } else if (c == DELETE_KEY || c == BACKSPACE_KEY) {
  7850. delete_at_cursor();
  7851. set_preferred_column();
  7852. redraw_text();
  7853. place_cursor_actual();
  7854. retval = 1;
  7855. goto _finish_completion;
  7856. }
  7857. /* TODO: Keyboard navigation of the matches list would be nice */
  7858. redraw_all();
  7859. break;
  7860. }
  7861. bim_unget(c);
  7862. _finish_completion:
  7863. for (int i = 0; i < matches_count; ++i) {
  7864. free_completion_match(&matches[i]);
  7865. }
  7866. free(matches);
  7867. free(tmp);
  7868. return retval;
  7869. }
  7870. /**
  7871. * Set the search string from a UTF-8 sequence.
  7872. * Since the search string is normally a series of codepoints, this saves
  7873. * some effort when trying to search for things we pulled from the outside world.
  7874. * (eg., ctags search terms)
  7875. */
  7876. void set_search_from_bytes(char * bytes) {
  7877. if (global_config.search) free(global_config.search);
  7878. global_config.search = malloc(sizeof(uint32_t) * (strlen(bytes) + 1));
  7879. uint32_t * s = global_config.search;
  7880. char * tmp = bytes;
  7881. uint32_t c, istate = 0;
  7882. while (*tmp) {
  7883. if (!decode(&istate, &c, *tmp)) {
  7884. *s = c;
  7885. s++;
  7886. *s = 0;
  7887. } else if (istate == UTF8_REJECT) {
  7888. istate = 0;
  7889. }
  7890. tmp++;
  7891. }
  7892. }
  7893. BIM_ACTION(goto_definition, 0,
  7894. "Jump to the definition of the word under under cursor."
  7895. )(void) {
  7896. uint32_t * word = get_word_under_cursor();
  7897. if (!word) {
  7898. render_error("No match");
  7899. return;
  7900. }
  7901. struct completion_match *matches;
  7902. int matches_count;
  7903. if (read_tags(word, &matches, &matches_count, 1)) {
  7904. render_error("No tags file");
  7905. goto _done;
  7906. }
  7907. if (!matches_count) {
  7908. render_error("No match");
  7909. goto _done;
  7910. }
  7911. #define _perform_correct_search(i) do { \
  7912. if (matches[i].search[0] == '/') { \
  7913. set_search_from_bytes(&matches[i].search[1]); \
  7914. search_next(); \
  7915. } else { \
  7916. goto_line(atoi(matches[i].search)); \
  7917. } \
  7918. } while (0)
  7919. if (env->file_name && !strcmp(matches[0].file, env->file_name)) {
  7920. _perform_correct_search(0);
  7921. } else {
  7922. /* Check if there were other matches that are in this file */
  7923. for (int i =1; env->file_name && i < matches_count; ++i) {
  7924. if (!strcmp(matches[i].file, env->file_name)) {
  7925. _perform_correct_search(i);
  7926. goto _done;
  7927. }
  7928. }
  7929. /* Check buffers */
  7930. for (int i = 0; i < buffers_len; ++i) {
  7931. if (buffers[i]->file_name && !strcmp(matches[0].file,buffers[i]->file_name)) {
  7932. if (left_buffer && buffers[i] != left_buffer && buffers[i] != right_buffer) unsplit();
  7933. env = buffers[i];
  7934. redraw_tabbar();
  7935. _perform_correct_search(i);
  7936. goto _done;
  7937. }
  7938. }
  7939. /* Okay, let's try opening */
  7940. buffer_t * old_buf = env;
  7941. open_file(matches[0].file);
  7942. if (env != old_buf) {
  7943. _perform_correct_search(0);
  7944. } else {
  7945. render_error("Could not locate file containing definition");
  7946. }
  7947. }
  7948. _done:
  7949. for (int i = 0; i < matches_count; ++i) {
  7950. free_completion_match(&matches[i]);
  7951. }
  7952. free(matches);
  7953. free(word);
  7954. }
  7955. /**
  7956. * Read one codepoint, with verbatim support.
  7957. */
  7958. int read_one_character(char * message) {
  7959. /* Read one character and replace */
  7960. if (!global_config.overlay_mode) {
  7961. render_commandline_message(message);
  7962. place_cursor_actual();
  7963. }
  7964. int c;
  7965. while ((c = bim_getkey(200))) {
  7966. if (c == KEY_TIMEOUT) continue;
  7967. if (c == KEY_CTRL_V) {
  7968. if (!global_config.overlay_mode) {
  7969. render_commandline_message(message);
  7970. printf(" ^V");
  7971. place_cursor_actual();
  7972. }
  7973. while ((c = bim_getch()) == -1);
  7974. break;
  7975. }
  7976. break;
  7977. }
  7978. redraw_commandline();
  7979. return c;
  7980. }
  7981. int read_one_byte(char * message) {
  7982. if (!global_config.overlay_mode) {
  7983. render_commandline_message(message);
  7984. place_cursor_actual();
  7985. }
  7986. int c;
  7987. while ((c = bim_getch())) {
  7988. if (c == -1) continue;
  7989. break;
  7990. }
  7991. redraw_commandline();
  7992. return c;
  7993. }
  7994. BIM_ACTION(cursor_left_with_wrap, 0,
  7995. "Move the cursor one position left, wrapping to the previous line."
  7996. )(void) {
  7997. if (env->line_no > 1 && env->col_no == 1) {
  7998. env->line_no--;
  7999. env->col_no = env->lines[env->line_no-1]->actual;
  8000. set_preferred_column();
  8001. place_cursor_actual();
  8002. } else {
  8003. cursor_left();
  8004. }
  8005. }
  8006. BIM_ACTION(prepend_and_insert, ACTION_IS_RW,
  8007. "Insert a new line before the current line and enter insert mode."
  8008. )(void) {
  8009. set_history_break();
  8010. env->lines = add_line(env->lines, env->line_no-1);
  8011. env->col_no = 1;
  8012. add_indent(env->line_no-1,env->line_no,0);
  8013. if (env->highlighting_paren && env->highlighting_paren > env->line_no) env->highlighting_paren++;
  8014. redraw_text();
  8015. set_preferred_column();
  8016. set_modified();
  8017. place_cursor_actual();
  8018. env->mode = MODE_INSERT;
  8019. }
  8020. BIM_ACTION(append_and_insert, ACTION_IS_RW,
  8021. "Insert a new line after the current line and enter insert mode."
  8022. )(void) {
  8023. set_history_break();
  8024. env->lines = add_line(env->lines, env->line_no);
  8025. env->col_no = 1;
  8026. env->line_no += 1;
  8027. add_indent(env->line_no-1,env->line_no-2,0);
  8028. set_preferred_column();
  8029. if (env->line_no > env->offset + global_config.term_height - global_config.bottom_size - 1) {
  8030. env->offset += 1;
  8031. }
  8032. if (env->highlighting_paren && env->highlighting_paren > env->line_no) env->highlighting_paren++;
  8033. redraw_text();
  8034. set_modified();
  8035. place_cursor_actual();
  8036. env->mode = MODE_INSERT;
  8037. }
  8038. BIM_ACTION(insert_after_cursor, ACTION_IS_RW,
  8039. "Place the cursor after the selected character and enter insert mode."
  8040. )(void) {
  8041. if (env->col_no < env->lines[env->line_no-1]->actual + 1) {
  8042. env->col_no += 1;
  8043. }
  8044. enter_insert();
  8045. }
  8046. BIM_ACTION(delete_forward, ACTION_IS_RW,
  8047. "Delete the character under the cursor."
  8048. )(void) {
  8049. if (env->col_no <= env->lines[env->line_no-1]->actual) {
  8050. line_delete(env->lines[env->line_no-1], env->col_no, env->line_no-1);
  8051. redraw_text();
  8052. } else if (env->col_no == env->lines[env->line_no-1]->actual + 1 && env->line_count > env->line_no) {
  8053. merge_lines(env->lines, env->line_no);
  8054. redraw_text();
  8055. }
  8056. set_modified();
  8057. redraw_statusbar();
  8058. place_cursor_actual();
  8059. }
  8060. BIM_ACTION(delete_forward_and_insert, ACTION_IS_RW,
  8061. "Delete the character under the cursor and enter insert mode."
  8062. )(void) {
  8063. set_history_break();
  8064. delete_forward();
  8065. env->mode = MODE_INSERT;
  8066. }
  8067. BIM_ACTION(paste, ARG_IS_CUSTOM | ACTION_IS_RW,
  8068. "Paste yanked text before (`P`) or after (`p`) the cursor."
  8069. )(int direction) {
  8070. if (global_config.yanks) {
  8071. if (!global_config.yank_is_full_lines) {
  8072. /* Handle P for paste before, p for past after */
  8073. int target_column = (direction == -1 ? (env->col_no) : (env->col_no+1));
  8074. if (target_column > env->lines[env->line_no-1]->actual + 1) {
  8075. target_column = env->lines[env->line_no-1]->actual + 1;
  8076. }
  8077. if (global_config.yank_count > 1) {
  8078. /* Spit the current line at the current position */
  8079. env->lines = split_line(env->lines, env->line_no - 1, target_column - 1); /* Split after */
  8080. }
  8081. /* Insert first line at current position */
  8082. for (int i = 0; i < global_config.yanks[0]->actual; ++i) {
  8083. env->lines[env->line_no - 1] = line_insert(env->lines[env->line_no - 1], global_config.yanks[0]->text[i], target_column + i - 1, env->line_no - 1);
  8084. }
  8085. if (global_config.yank_count > 1) {
  8086. /* Insert full lines */
  8087. for (unsigned int i = 1; i < global_config.yank_count - 1; ++i) {
  8088. env->lines = add_line(env->lines, env->line_no);
  8089. }
  8090. for (unsigned int i = 1; i < global_config.yank_count - 1; ++i) {
  8091. replace_line(env->lines, env->line_no + i - 1, global_config.yanks[i]);
  8092. }
  8093. /* Insert characters from last line into (what was) the next line */
  8094. for (int i = 0; i < global_config.yanks[global_config.yank_count-1]->actual; ++i) {
  8095. env->lines[env->line_no + global_config.yank_count - 2] = line_insert(env->lines[env->line_no + global_config.yank_count - 2], global_config.yanks[global_config.yank_count-1]->text[i], i, env->line_no + global_config.yank_count - 2);
  8096. }
  8097. }
  8098. } else {
  8099. /* Insert full lines */
  8100. for (unsigned int i = 0; i < global_config.yank_count; ++i) {
  8101. env->lines = add_line(env->lines, env->line_no - (direction == -1 ? 1 : 0));
  8102. }
  8103. for (unsigned int i = 0; i < global_config.yank_count; ++i) {
  8104. replace_line(env->lines, env->line_no - (direction == -1 ? 1 : 0) + i, global_config.yanks[i]);
  8105. }
  8106. }
  8107. /* Recalculate whole document syntax */
  8108. for (int i = 0; i < env->line_count; ++i) {
  8109. env->lines[i]->istate = 0;
  8110. }
  8111. for (int i = 0; i < env->line_count; ++i) {
  8112. recalculate_syntax(env->lines[i],i);
  8113. }
  8114. if (direction == 1) {
  8115. if (global_config.yank_is_full_lines) {
  8116. env->line_no += 1;
  8117. } else {
  8118. if (global_config.yank_count == 1) {
  8119. env->col_no = env->col_no + global_config.yanks[0]->actual;
  8120. } else {
  8121. env->line_no = env->line_no + global_config.yank_count - 1;
  8122. env->col_no = global_config.yanks[global_config.yank_count-1]->actual;
  8123. }
  8124. }
  8125. }
  8126. if (global_config.yank_is_full_lines) {
  8127. env->col_no = 1;
  8128. for (int i = 0; i < env->lines[env->line_no-1]->actual; ++i) {
  8129. if (!is_whitespace(env->lines[env->line_no-1]->text[i].codepoint)) {
  8130. env->col_no = i + 1;
  8131. break;
  8132. }
  8133. }
  8134. }
  8135. set_history_break();
  8136. set_modified();
  8137. redraw_all();
  8138. }
  8139. }
  8140. BIM_ACTION(insert_at_end, ACTION_IS_RW,
  8141. "Move the cursor to the end of the current line and enter insert mode."
  8142. )(void) {
  8143. env->col_no = env->lines[env->line_no-1]->actual+1;
  8144. env->mode = MODE_INSERT;
  8145. set_history_break();
  8146. }
  8147. BIM_ACTION(enter_replace, ACTION_IS_RW,
  8148. "Enter replace mode."
  8149. )(void) {
  8150. env->mode = MODE_REPLACE;
  8151. set_history_break();
  8152. }
  8153. BIM_ACTION(toggle_numbers, 0,
  8154. "Toggle the display of line numbers."
  8155. )(void) {
  8156. env->numbers = !env->numbers;
  8157. redraw_all();
  8158. place_cursor_actual();
  8159. }
  8160. BIM_ACTION(toggle_gutter, 0,
  8161. "Toggle the display of the revision status gutter."
  8162. )(void) {
  8163. env->gutter = !env->gutter;
  8164. redraw_all();
  8165. place_cursor_actual();
  8166. }
  8167. BIM_ACTION(toggle_indent, 0,
  8168. "Toggle smart indentation."
  8169. )(void) {
  8170. env->indent = !env->indent;
  8171. redraw_statusbar();
  8172. place_cursor_actual();
  8173. }
  8174. BIM_ACTION(toggle_smartcomplete, 0,
  8175. "Toggle smart completion."
  8176. )(void) {
  8177. global_config.smart_complete = !global_config.smart_complete;
  8178. redraw_statusbar();
  8179. place_cursor_actual();
  8180. }
  8181. BIM_ACTION(expand_split_right, 0,
  8182. "Move the view split divider to the right."
  8183. )(void) {
  8184. global_config.split_percent += 1;
  8185. update_split_size();
  8186. redraw_all();
  8187. }
  8188. BIM_ACTION(expand_split_left, 0,
  8189. "Move the view split divider to the left."
  8190. )(void) {
  8191. global_config.split_percent -= 1;
  8192. update_split_size();
  8193. redraw_all();
  8194. }
  8195. BIM_ACTION(go_page_up, 0,
  8196. "Jump up a screenful."
  8197. )(void) {
  8198. goto_line(env->line_no - (global_config.term_height - 6));
  8199. }
  8200. BIM_ACTION(go_page_down, 0,
  8201. "Jump down a screenful."
  8202. )(void) {
  8203. goto_line(env->line_no + (global_config.term_height - 6));
  8204. }
  8205. BIM_ACTION(jump_to_matching_bracket, 0,
  8206. "Find and jump to the matching bracket for the character under the cursor."
  8207. )(void) {
  8208. recalculate_selected_lines();
  8209. int paren_line = -1, paren_col = -1;
  8210. find_matching_paren(&paren_line, &paren_col, 1);
  8211. if (paren_line != -1) {
  8212. env->line_no = paren_line;
  8213. env->col_no = paren_col;
  8214. set_preferred_column();
  8215. place_cursor_actual();
  8216. redraw_statusbar();
  8217. }
  8218. }
  8219. BIM_ACTION(jump_to_previous_blank, 0,
  8220. "Jump to the preceding blank line before the cursor."
  8221. )(void) {
  8222. env->col_no = 1;
  8223. if (env->line_no == 1) return;
  8224. do {
  8225. env->line_no--;
  8226. if (env->lines[env->line_no-1]->actual == 0) break;
  8227. } while (env->line_no > 1);
  8228. set_preferred_column();
  8229. redraw_statusbar();
  8230. }
  8231. BIM_ACTION(jump_to_next_blank, 0,
  8232. "Jump to the next blank line after the cursor."
  8233. )(void) {
  8234. env->col_no = 1;
  8235. if (env->line_no == env->line_count) return;
  8236. do {
  8237. env->line_no++;
  8238. if (env->lines[env->line_no-1]->actual == 0) break;
  8239. } while (env->line_no < env->line_count);
  8240. set_preferred_column();
  8241. redraw_statusbar();
  8242. }
  8243. BIM_ACTION(first_non_whitespace, 0,
  8244. "Jump to the first non-whitespace character in the current line."
  8245. )(void) {
  8246. for (int i = 0; i < env->lines[env->line_no-1]->actual; ++i) {
  8247. if (!is_whitespace(env->lines[env->line_no-1]->text[i].codepoint)) {
  8248. env->col_no = i + 1;
  8249. break;
  8250. }
  8251. }
  8252. set_preferred_column();
  8253. redraw_statusbar();
  8254. }
  8255. BIM_ACTION(next_line_non_whitespace, 0,
  8256. "Jump to the first non-whitespace character in the next next line."
  8257. )(void) {
  8258. if (env->line_no < env->line_count) {
  8259. env->line_no++;
  8260. env->col_no = 1;
  8261. } else {
  8262. return;
  8263. }
  8264. first_non_whitespace();
  8265. }
  8266. BIM_ACTION(smart_backspace, ACTION_IS_RW,
  8267. "Delete the preceding character, with special handling for indentation."
  8268. )(void) {
  8269. if (!env->tabs && env->col_no > 1) {
  8270. int i;
  8271. for (i = 0; i < env->col_no-1; ++i) {
  8272. if (!is_whitespace(env->lines[env->line_no-1]->text[i].codepoint)) break;
  8273. }
  8274. if (i == env->col_no-1) {
  8275. /* Backspace until aligned */
  8276. delete_at_cursor();
  8277. while (env->col_no > 1 && (env->col_no-1) % env->tabstop) {
  8278. delete_at_cursor();
  8279. }
  8280. return;
  8281. }
  8282. }
  8283. delete_at_cursor();
  8284. }
  8285. BIM_ACTION(perform_omni_completion, ACTION_IS_RW,
  8286. "(temporary) Perform smart symbol competion from ctags."
  8287. )(void) {
  8288. /* This should probably be a submode */
  8289. while (omni_complete(0) == 1);
  8290. }
  8291. BIM_ACTION(smart_tab, ACTION_IS_RW,
  8292. "Insert a tab or spaces depending on indent mode. (Use ^V <tab> to guarantee a literal tab)"
  8293. )(void) {
  8294. if (env->tabs) {
  8295. insert_char('\t');
  8296. } else {
  8297. for (int i = 0; i < env->tabstop; ++i) {
  8298. insert_char(' ');
  8299. }
  8300. }
  8301. }
  8302. BIM_ACTION(smart_comment_end, ARG_IS_INPUT | ACTION_IS_RW,
  8303. "Insert a `/` ending a C-style comment."
  8304. )(int c) {
  8305. /* smart *end* of comment anyway */
  8306. if (env->indent) {
  8307. if ((env->lines[env->line_no-1]->text[env->col_no-2].flags & 0x1F) == FLAG_COMMENT &&
  8308. (env->lines[env->line_no-1]->text[env->col_no-2].codepoint == ' ') &&
  8309. (env->col_no > 3) &&
  8310. (env->lines[env->line_no-1]->text[env->col_no-3].codepoint == '*')) {
  8311. env->col_no--;
  8312. replace_char('/');
  8313. env->col_no++;
  8314. place_cursor_actual();
  8315. return;
  8316. }
  8317. }
  8318. insert_char(c);
  8319. }
  8320. BIM_ACTION(smart_brace_end, ARG_IS_INPUT | ACTION_IS_RW,
  8321. "Insert a closing brace and smartly position it if it is the first character on a line."
  8322. )(int c) {
  8323. if (env->indent) {
  8324. int was_whitespace = 1;
  8325. for (int i = 0; i < env->lines[env->line_no-1]->actual; ++i) {
  8326. if (env->lines[env->line_no-1]->text[i].codepoint != ' ' &&
  8327. env->lines[env->line_no-1]->text[i].codepoint != '\t') {
  8328. was_whitespace = 0;
  8329. break;
  8330. }
  8331. }
  8332. insert_char(c);
  8333. if (was_whitespace) {
  8334. int line = -1, col = -1;
  8335. env->col_no--;
  8336. find_matching_paren(&line,&col, 1);
  8337. if (line != -1) {
  8338. line = find_brace_line_start(line, col);
  8339. while (env->lines[env->line_no-1]->actual) {
  8340. line_delete(env->lines[env->line_no-1], env->lines[env->line_no-1]->actual, env->line_no-1);
  8341. }
  8342. add_indent(env->line_no-1,line-1,1);
  8343. env->col_no = env->lines[env->line_no-1]->actual + 1;
  8344. insert_char(c);
  8345. }
  8346. }
  8347. set_preferred_column();
  8348. return;
  8349. }
  8350. insert_char(c);
  8351. }
  8352. BIM_ACTION(enter_line_selection_and_cursor_up, 0,
  8353. "Enter line selection and move the cursor up one line."
  8354. )(void) {
  8355. enter_line_selection();
  8356. cursor_up();
  8357. }
  8358. BIM_ACTION(enter_line_selection_and_cursor_down, 0,
  8359. "Enter line selection and move the cursor down one line."
  8360. )(void) {
  8361. enter_line_selection();
  8362. cursor_down();
  8363. }
  8364. BIM_ACTION(shift_horizontally, ARG_IS_CUSTOM,
  8365. "Shift the current line or screen view horiztonally, depending on settings."
  8366. )(int amount) {
  8367. env->coffset += amount;
  8368. if (env->coffset < 0) env->coffset = 0;
  8369. redraw_text();
  8370. }
  8371. static int state_before_paste = 0;
  8372. BIM_ACTION(paste_begin, 0, "Begin bracketed paste; disable indentation, completion, etc.")(void) {
  8373. if (global_config.smart_complete) state_before_paste |= 0x01;
  8374. if (env->indent) state_before_paste |= 0x02;
  8375. global_config.smart_complete = 0;
  8376. env->indent = 0;
  8377. /* TODO: We need env->loading == 1, but with history (manual breaks, though) */
  8378. }
  8379. BIM_ACTION(paste_end, 0, "End bracketed paste; restore indentation, completion, etc.")(void) {
  8380. if (state_before_paste & 0x01) global_config.smart_complete = 1;
  8381. if (state_before_paste & 0x02) env->indent = 1;
  8382. redraw_statusbar();
  8383. }
  8384. struct action_map _NORMAL_MAP[] = {
  8385. {KEY_BACKSPACE, cursor_left_with_wrap, opt_rep, 0},
  8386. {'V', enter_line_selection, 0, 0},
  8387. {'v', enter_char_selection, 0, 0},
  8388. {KEY_CTRL_V, enter_col_selection, 0, 0},
  8389. {'O', prepend_and_insert, opt_rw, 0},
  8390. {'o', append_and_insert, opt_rw, 0},
  8391. {'a', insert_after_cursor, opt_rw, 0},
  8392. {'s', delete_forward_and_insert, opt_rw, 0},
  8393. {'x', delete_forward, opt_rep | opt_rw, 0},
  8394. {'P', paste, opt_arg | opt_rw, -1},
  8395. {'p', paste, opt_arg | opt_rw, 1},
  8396. {'r', replace_char, opt_char | opt_rw, 0},
  8397. {'A', insert_at_end, opt_rw, 0},
  8398. {'u', undo_history, opt_rw, 0},
  8399. {KEY_CTRL_R, redo_history, opt_rw, 0},
  8400. {KEY_CTRL_L, redraw_all, 0, 0},
  8401. {KEY_CTRL_G, goto_definition, 0, 0},
  8402. {'i', enter_insert, opt_rw, 0},
  8403. {'R', enter_replace, opt_rw, 0},
  8404. {KEY_SHIFT_UP, enter_line_selection_and_cursor_up, 0, 0},
  8405. {KEY_SHIFT_DOWN, enter_line_selection_and_cursor_down, 0, 0},
  8406. {KEY_ALT_UP, previous_tab, 0, 0},
  8407. {KEY_ALT_DOWN, next_tab, 0, 0},
  8408. {-1, NULL, 0, 0},
  8409. };
  8410. struct action_map _INSERT_MAP[] = {
  8411. {KEY_ESCAPE, leave_insert, 0, 0},
  8412. {KEY_DELETE, delete_forward, 0, 0},
  8413. {KEY_CTRL_C, leave_insert, 0, 0},
  8414. {KEY_BACKSPACE, smart_backspace, 0, 0},
  8415. {KEY_ENTER, insert_line_feed, 0, 0},
  8416. {KEY_CTRL_O, perform_omni_completion, 0, 0},
  8417. {KEY_CTRL_V, insert_char, opt_byte, 0},
  8418. {KEY_CTRL_W, delete_word, 0, 0},
  8419. {'\t', smart_tab, 0, 0},
  8420. {'/', smart_comment_end, opt_arg, '/'},
  8421. {'}', smart_brace_end, opt_arg, '}'},
  8422. {KEY_PASTE_BEGIN, paste_begin, 0, 0},
  8423. {KEY_PASTE_END, paste_end, 0, 0},
  8424. {-1, NULL, 0, 0},
  8425. };
  8426. struct action_map _REPLACE_MAP[] = {
  8427. {KEY_ESCAPE, leave_insert, 0, 0},
  8428. {KEY_DELETE, delete_forward, 0, 0},
  8429. {KEY_BACKSPACE, cursor_left_with_wrap, 0, 0},
  8430. {KEY_ENTER, insert_line_feed, 0, 0},
  8431. {-1, NULL, 0, 0},
  8432. };
  8433. struct action_map _LINE_SELECTION_MAP[] = {
  8434. {KEY_ESCAPE, leave_selection, 0, 0},
  8435. {KEY_CTRL_C, leave_selection, 0, 0},
  8436. {'V', leave_selection, 0, 0},
  8437. {'v', switch_selection_mode, opt_arg, MODE_CHAR_SELECTION},
  8438. {'y', yank_lines, opt_norm, 0},
  8439. {KEY_BACKSPACE, cursor_left_with_wrap, 0, 0},
  8440. {'\t', adjust_indent, opt_arg | opt_rw, 1},
  8441. {KEY_SHIFT_TAB, adjust_indent, opt_arg | opt_rw, -1},
  8442. {'D', delete_and_yank_lines, opt_rw | opt_norm, 0},
  8443. {'d', delete_and_yank_lines, opt_rw | opt_norm, 0},
  8444. {'x', delete_and_yank_lines, opt_rw | opt_norm, 0},
  8445. {'s', delete_lines_and_enter_insert, opt_rw, 0},
  8446. {'r', replace_chars_in_line, opt_char | opt_rw, 0},
  8447. {KEY_SHIFT_UP, cursor_up, 0, 0},
  8448. {KEY_SHIFT_DOWN, cursor_down, 0, 0},
  8449. {-1, NULL, 0, 0},
  8450. };
  8451. struct action_map _CHAR_SELECTION_MAP[] = {
  8452. {KEY_ESCAPE, leave_selection, 0, 0},
  8453. {KEY_CTRL_C, leave_selection, 0, 0},
  8454. {'v', leave_selection, 0, 0},
  8455. {'V', switch_selection_mode, opt_arg, MODE_LINE_SELECTION},
  8456. {'y', yank_characters, opt_norm, 0},
  8457. {KEY_BACKSPACE, cursor_left_with_wrap, 0, 0},
  8458. {'D', delete_and_yank_chars, opt_rw | opt_norm, 0},
  8459. {'d', delete_and_yank_chars, opt_rw | opt_norm, 0},
  8460. {'x', delete_and_yank_chars, opt_rw | opt_norm, 0},
  8461. {'s', delete_chars_and_enter_insert, opt_rw, 0},
  8462. {'r', replace_chars, opt_char | opt_rw, 0},
  8463. {'A', insert_at_end_of_selection, opt_rw, 0},
  8464. {-1, NULL, 0, 0},
  8465. };
  8466. struct action_map _COL_SELECTION_MAP[] = {
  8467. {KEY_ESCAPE, leave_selection, 0, 0},
  8468. {KEY_CTRL_C, leave_selection, 0, 0},
  8469. {KEY_CTRL_V, leave_selection, 0, 0},
  8470. {'I', enter_col_insert, opt_rw, 0},
  8471. {'a', enter_col_insert_after, opt_rw, 0},
  8472. {'d', delete_column, opt_norm | opt_rw, 0},
  8473. {-1, NULL, 0, 0},
  8474. };
  8475. struct action_map _COL_INSERT_MAP[] = {
  8476. {KEY_ESCAPE, leave_selection, 0, 0},
  8477. {KEY_CTRL_C, leave_selection, 0, 0},
  8478. {KEY_BACKSPACE, delete_at_column, opt_arg, -1},
  8479. {KEY_DELETE, delete_at_column, opt_arg, 1},
  8480. {KEY_ENTER, NULL, 0, 0},
  8481. {KEY_CTRL_W, NULL, 0, 0},
  8482. {KEY_CTRL_V, insert_char_at_column, opt_char, 0},
  8483. {-1, NULL, 0, 0},
  8484. };
  8485. struct action_map _NAVIGATION_MAP[] = {
  8486. /* Common navigation */
  8487. {KEY_CTRL_B, go_page_up, opt_rep, 0},
  8488. {KEY_CTRL_F, go_page_down, opt_rep, 0},
  8489. {':', enter_command, 0, 0},
  8490. {'/', enter_search, opt_arg, 1},
  8491. {'?', enter_search, opt_arg, 0},
  8492. {'n', search_next, opt_rep, 0},
  8493. {'N', search_prev, opt_rep, 0},
  8494. {'j', cursor_down, opt_rep, 0},
  8495. {'k', cursor_up, opt_rep, 0},
  8496. {'h', cursor_left, opt_rep, 0},
  8497. {'l', cursor_right, opt_rep, 0},
  8498. {'b', word_left, opt_rep, 0},
  8499. {'w', word_right, opt_rep, 0},
  8500. {'B', big_word_left, opt_rep, 0},
  8501. {'W', big_word_right, opt_rep, 0},
  8502. {'<', shift_horizontally, opt_arg, -1},
  8503. {'>', shift_horizontally, opt_arg, 1},
  8504. {'f', find_character_forward, opt_rep | opt_arg | opt_char, 'f'},
  8505. {'F', find_character_backward, opt_rep | opt_arg | opt_char, 'F'},
  8506. {'t', find_character_forward, opt_rep | opt_arg | opt_char, 't'},
  8507. {'T', find_character_backward, opt_rep | opt_arg | opt_char, 'T'},
  8508. {'G', goto_line, opt_nav, 0},
  8509. {'*', search_under_cursor, 0, 0},
  8510. {' ', go_page_down, opt_rep, 0},
  8511. {'%', jump_to_matching_bracket, 0, 0},
  8512. {'{', jump_to_previous_blank, opt_rep, 0},
  8513. {'}', jump_to_next_blank, opt_rep, 0},
  8514. {'$', cursor_end, 0, 0},
  8515. {'|', cursor_home, 0, 0},
  8516. {KEY_ENTER, next_line_non_whitespace, opt_rep, 0},
  8517. {'^', first_non_whitespace, 0, 0},
  8518. {'0', cursor_home, 0, 0},
  8519. {-1, NULL, 0, 0},
  8520. };
  8521. struct action_map _ESCAPE_MAP[] = {
  8522. {KEY_F1, toggle_numbers, 0, 0},
  8523. {KEY_F2, toggle_indent, 0, 0},
  8524. {KEY_F3, toggle_gutter, 0, 0},
  8525. {KEY_F4, toggle_smartcomplete, 0, 0},
  8526. {KEY_MOUSE, handle_mouse, 0, 0},
  8527. {KEY_MOUSE_SGR, handle_mouse_sgr, 0, 0},
  8528. {KEY_UP, cursor_up, opt_rep, 0},
  8529. {KEY_DOWN, cursor_down, opt_rep, 0},
  8530. {KEY_RIGHT, cursor_right, opt_rep, 0},
  8531. {KEY_CTRL_RIGHT, big_word_right, opt_rep, 0},
  8532. {KEY_SHIFT_RIGHT, word_right, opt_rep, 0},
  8533. {KEY_ALT_RIGHT, expand_split_right, opt_rep, 0},
  8534. {KEY_ALT_SHIFT_RIGHT, use_right_buffer, opt_rep, 0},
  8535. {KEY_LEFT, cursor_left, opt_rep, 0},
  8536. {KEY_CTRL_LEFT, big_word_left, opt_rep, 0},
  8537. {KEY_SHIFT_LEFT, word_left, opt_rep, 0},
  8538. {KEY_ALT_LEFT, expand_split_left, opt_rep, 0},
  8539. {KEY_ALT_SHIFT_LEFT, use_left_buffer, opt_rep, 0},
  8540. {KEY_HOME, cursor_home, 0, 0},
  8541. {KEY_END, cursor_end, 0, 0},
  8542. {KEY_PAGE_UP, go_page_up, opt_rep, 0},
  8543. {KEY_PAGE_DOWN, go_page_down, opt_rep, 0},
  8544. {KEY_CTRL_Z, suspend, 0, 0},
  8545. {-1, NULL, 0, 0}
  8546. };
  8547. struct action_map _COMMAND_MAP[] = {
  8548. {KEY_ENTER, command_accept, 0, 0},
  8549. {'\t', command_tab_complete_buffer, 0, 0},
  8550. {KEY_UP, command_scroll_history, opt_arg, -1}, /* back */
  8551. {KEY_DOWN, command_scroll_history, opt_arg, 1}, /* forward */
  8552. {-1, NULL, 0, 0}
  8553. };
  8554. struct action_map _SEARCH_MAP[] = {
  8555. {KEY_ENTER, search_accept, 0, 0},
  8556. {KEY_UP, NULL, 0, 0},
  8557. {KEY_DOWN, NULL, 0, 0},
  8558. {-1, NULL, 0, 0}
  8559. };
  8560. struct action_map _INPUT_BUFFER_MAP[] = {
  8561. /* These are generic and shared with search */
  8562. {KEY_ESCAPE, command_discard, 0, 0},
  8563. {KEY_CTRL_C, command_discard, 0, 0},
  8564. {KEY_BACKSPACE, command_backspace, 0, 0},
  8565. {KEY_CTRL_W, command_word_delete, 0, 0},
  8566. {KEY_MOUSE, eat_mouse, 0, 0},
  8567. {KEY_LEFT, command_cursor_left, 0, 0},
  8568. {KEY_CTRL_LEFT, command_word_left, 0, 0},
  8569. {KEY_RIGHT, command_cursor_right, 0, 0},
  8570. {KEY_CTRL_RIGHT,command_word_right, 0, 0},
  8571. {KEY_HOME, command_cursor_home, 0, 0},
  8572. {KEY_END, command_cursor_end, 0, 0},
  8573. {-1, NULL, 0, 0}
  8574. };
  8575. /* DIRECTORY_BROWSE_MAP is only to override KEY_ENTER and should not be remapped,
  8576. * so unlike the others it is not going to be redefined as a pointer. */
  8577. struct action_map DIRECTORY_BROWSE_MAP[] = {
  8578. {KEY_ENTER, open_file_from_line, 0, 0},
  8579. {-1, NULL, 0, 0}
  8580. };
  8581. struct action_map * NORMAL_MAP = NULL;
  8582. struct action_map * INSERT_MAP = NULL;
  8583. struct action_map * REPLACE_MAP = NULL;
  8584. struct action_map * LINE_SELECTION_MAP = NULL;
  8585. struct action_map * CHAR_SELECTION_MAP = NULL;
  8586. struct action_map * COL_SELECTION_MAP = NULL;
  8587. struct action_map * COL_INSERT_MAP = NULL;
  8588. struct action_map * NAVIGATION_MAP = NULL;
  8589. struct action_map * ESCAPE_MAP = NULL;
  8590. struct action_map * COMMAND_MAP = NULL;
  8591. struct action_map * SEARCH_MAP = NULL;
  8592. struct action_map * INPUT_BUFFER_MAP = NULL;
  8593. struct mode_names mode_names[] = {
  8594. {"Normal","norm",&NORMAL_MAP},
  8595. {"Insert","insert",&INSERT_MAP},
  8596. {"Replace","replace",&REPLACE_MAP},
  8597. {"Line Selection","line",&LINE_SELECTION_MAP},
  8598. {"Char Selection","char",&CHAR_SELECTION_MAP},
  8599. {"Col Selection","col",&COL_SELECTION_MAP},
  8600. {"Col Insert","colinsert",&COL_INSERT_MAP},
  8601. {"Navigation (Select)","nav",&NAVIGATION_MAP},
  8602. {"Escape (Select, Insert)","esc",&ESCAPE_MAP},
  8603. {"Command","command",&COMMAND_MAP},
  8604. {"Search","search",&SEARCH_MAP},
  8605. {"Input (Command, Search)","input",&INPUT_BUFFER_MAP},
  8606. {NULL,NULL,NULL},
  8607. };
  8608. int handle_action(struct action_map * basemap, int key) {
  8609. for (struct action_map * map = basemap; map->key != -1; map++) {
  8610. if (map->key == key) {
  8611. if (!map->method) return 1;
  8612. if ((map->options & opt_rw) && (env->readonly)) {
  8613. render_error("Buffer is read-only");
  8614. return 2;
  8615. }
  8616. /* Determine how to format this request */
  8617. int reps = (map->options & opt_rep) ? ((nav_buffer) ? atoi(nav_buf) : 1) : 1;
  8618. int c = 0;
  8619. if (map->options & opt_char) {
  8620. c = read_one_character(name_from_key(key));
  8621. }
  8622. if (map->options & opt_byte) {
  8623. c = read_one_byte(name_from_key(key));
  8624. }
  8625. for (int i = 0; i < reps; ++i) {
  8626. if (((map->options & opt_char) || (map->options & opt_byte)) && (map->options & opt_arg)) {
  8627. map->method(map->arg, c);
  8628. } else if ((map->options & opt_char) || (map->options & opt_byte)) {
  8629. map->method(c);
  8630. } else if (map->options & opt_arg) {
  8631. map->method(map->arg);
  8632. } else if (map->options & opt_nav) {
  8633. if (nav_buffer) {
  8634. map->method(atoi(nav_buf));
  8635. reset_nav_buffer(0);
  8636. } else {
  8637. map->method(-1);
  8638. }
  8639. } else {
  8640. map->method();
  8641. }
  8642. }
  8643. if (map->options & opt_norm) {
  8644. if (env->mode == MODE_INSERT || env->mode == MODE_REPLACE) leave_insert();
  8645. else if (env->mode == MODE_LINE_SELECTION || env->mode == MODE_CHAR_SELECTION || env->mode == MODE_COL_SELECTION) leave_selection();
  8646. else {
  8647. env->mode = MODE_NORMAL;
  8648. redraw_all();
  8649. }
  8650. }
  8651. return 1;
  8652. }
  8653. }
  8654. return 0;
  8655. }
  8656. int handle_nav_buffer(int key) {
  8657. if ((key >= '1' && key <= '9') || (key == '0' && nav_buffer)) {
  8658. if (nav_buffer < NAV_BUFFER_MAX) {
  8659. /* Up to NAV_BUFFER_MAX=10 characters; that should be enough for most tasks */
  8660. nav_buf[nav_buffer] = key;
  8661. nav_buf[nav_buffer+1] = 0;
  8662. nav_buffer++;
  8663. /* Print the number buffer */
  8664. redraw_commandline();
  8665. }
  8666. return 0;
  8667. }
  8668. return 1;
  8669. }
  8670. /**
  8671. * NORMAL mode
  8672. *
  8673. * Default editor mode - just cursor navigation and keybinds
  8674. * to enter the other modes.
  8675. */
  8676. void normal_mode(void) {
  8677. int last_mode = MODE_NORMAL;
  8678. int refresh = 0;
  8679. while (1) {
  8680. if (global_config.overlay_mode) {
  8681. if (global_config.overlay_mode == OVERLAY_MODE_COMMAND) {
  8682. if (refresh) {
  8683. render_command_input_buffer();
  8684. refresh = 0;
  8685. }
  8686. int key = bim_getkey(200);
  8687. if (key != KEY_TIMEOUT) {
  8688. refresh = 1;
  8689. if (!handle_action(COMMAND_MAP, key))
  8690. if (!handle_action(INPUT_BUFFER_MAP, key))
  8691. if (key < KEY_ESCAPE) command_insert_char(key);
  8692. }
  8693. continue;
  8694. } else if (global_config.overlay_mode == OVERLAY_MODE_SEARCH) {
  8695. if (refresh) {
  8696. render_command_input_buffer();
  8697. refresh = 0;
  8698. }
  8699. int key = bim_getkey(200);
  8700. if (key != KEY_TIMEOUT) {
  8701. refresh = 1;
  8702. if (!handle_action(SEARCH_MAP, key)) {
  8703. if (!handle_action(INPUT_BUFFER_MAP, key)) {
  8704. if (key < KEY_ESCAPE) command_insert_char(key);
  8705. }
  8706. }
  8707. if (global_config.overlay_mode == OVERLAY_MODE_SEARCH) {
  8708. /* Find the next search match */
  8709. uint32_t * buffer = malloc(sizeof(uint32_t) * (global_config.command_buffer->actual+1));
  8710. for (int i = 0; i < global_config.command_buffer->actual; ++i) {
  8711. buffer[i] = global_config.command_buffer->text[i].codepoint;
  8712. }
  8713. buffer[global_config.command_buffer->actual] = 0;
  8714. int line = -1, col = -1;
  8715. if (global_config.search_direction == 1) {
  8716. find_match(global_config.prev_line, global_config.prev_col, &line, &col, buffer, NULL);
  8717. if (line == -1 && global_config.search_wraps) {
  8718. find_match(1, 1, &line, &col, buffer, NULL);
  8719. }
  8720. } else {
  8721. find_match_backwards(global_config.prev_line, global_config.prev_col, &line, &col, buffer);
  8722. if (line == -1 && global_config.search_wraps) {
  8723. find_match_backwards(env->line_count, env->lines[env->line_count-1]->actual, &line, &col, buffer);
  8724. }
  8725. }
  8726. if (line != -1) {
  8727. env->col_no = col;
  8728. env->line_no = line;
  8729. set_preferred_column();
  8730. } else {
  8731. env->coffset = global_config.prev_coffset;
  8732. env->offset = global_config.prev_offset;
  8733. env->col_no = global_config.prev_col;
  8734. set_preferred_column();
  8735. env->line_no = global_config.prev_line;
  8736. }
  8737. draw_search_match(buffer, 0);
  8738. free(buffer);
  8739. }
  8740. }
  8741. continue;
  8742. }
  8743. }
  8744. if (env->mode != last_mode) {
  8745. redraw_statusbar();
  8746. redraw_commandline();
  8747. last_mode = env->mode;
  8748. }
  8749. if (env->mode == MODE_NORMAL) {
  8750. place_cursor_actual();
  8751. int key = bim_getkey(200);
  8752. if (handle_nav_buffer(key)) {
  8753. if (!handle_action(NORMAL_MAP, key))
  8754. if (!handle_action(NAVIGATION_MAP, key))
  8755. handle_action(ESCAPE_MAP, key);
  8756. }
  8757. reset_nav_buffer(key);
  8758. } else if (env->mode == MODE_INSERT) {
  8759. place_cursor_actual();
  8760. int key = bim_getkey(refresh ? 10 : 200);
  8761. if (key == KEY_TIMEOUT) {
  8762. if (refresh > 1) {
  8763. redraw_text();
  8764. } else if (refresh) {
  8765. redraw_line(env->line_no-1);
  8766. }
  8767. refresh = 0;
  8768. } else if (handle_action(INSERT_MAP, key)) {
  8769. refresh = 2;
  8770. } else if (handle_action(ESCAPE_MAP, key)) {
  8771. /* Do nothing */
  8772. } else if (key < KEY_ESCAPE) {
  8773. insert_char(key);
  8774. if (global_config.smart_complete) {
  8775. redraw_line(env->line_no-1);
  8776. while (omni_complete(1) == 1);
  8777. }
  8778. refresh |= 1;
  8779. }
  8780. } else if (env->mode == MODE_REPLACE) {
  8781. place_cursor_actual();
  8782. int key = bim_getkey(200);
  8783. if (key != KEY_TIMEOUT) {
  8784. if (handle_action(REPLACE_MAP, key)) {
  8785. redraw_text();
  8786. } else if (!handle_action(ESCAPE_MAP, key)) {
  8787. /* Perform replacement */
  8788. if (key < KEY_ESCAPE) {
  8789. if (env->col_no <= env->lines[env->line_no - 1]->actual) {
  8790. replace_char(key);
  8791. env->col_no += 1;
  8792. } else {
  8793. insert_char(key);
  8794. redraw_line(env->line_no-1);
  8795. }
  8796. set_preferred_column();
  8797. }
  8798. }
  8799. }
  8800. } else if (env->mode == MODE_LINE_SELECTION) {
  8801. place_cursor_actual();
  8802. int key = bim_getkey(200);
  8803. if (key == KEY_TIMEOUT) continue;
  8804. if (handle_nav_buffer(key)) {
  8805. if (!handle_action(LINE_SELECTION_MAP, key))
  8806. if (!handle_action(NAVIGATION_MAP, key))
  8807. handle_action(ESCAPE_MAP, key);
  8808. }
  8809. reset_nav_buffer(key);
  8810. if (env->mode == MODE_LINE_SELECTION) {
  8811. /* Mark current line */
  8812. _redraw_line(env->line_no,0);
  8813. _redraw_line(env->start_line,1);
  8814. /* Properly mark everything in the span we just moved through */
  8815. if (env->prev_line < env->line_no) {
  8816. for (int i = env->prev_line; i < env->line_no; ++i) {
  8817. _redraw_line(i,0);
  8818. }
  8819. env->prev_line = env->line_no;
  8820. } else if (env->prev_line > env->line_no) {
  8821. for (int i = env->line_no + 1; i <= env->prev_line; ++i) {
  8822. _redraw_line(i,0);
  8823. }
  8824. env->prev_line = env->line_no;
  8825. }
  8826. redraw_commandline();
  8827. }
  8828. } else if (env->mode == MODE_CHAR_SELECTION) {
  8829. place_cursor_actual();
  8830. int key = bim_getkey(200);
  8831. if (key == KEY_TIMEOUT) continue;
  8832. if (handle_nav_buffer(key)) {
  8833. if (!handle_action(CHAR_SELECTION_MAP, key))
  8834. if (!handle_action(NAVIGATION_MAP, key))
  8835. handle_action(ESCAPE_MAP, key);
  8836. }
  8837. reset_nav_buffer(key);
  8838. if (env->mode == MODE_CHAR_SELECTION) {
  8839. /* Mark current line */
  8840. _redraw_line_char(env->line_no,1);
  8841. /* Properly mark everything in the span we just moved through */
  8842. if (env->prev_line < env->line_no) {
  8843. for (int i = env->prev_line; i < env->line_no; ++i) {
  8844. _redraw_line_char(i,1);
  8845. }
  8846. env->prev_line = env->line_no;
  8847. } else if (env->prev_line > env->line_no) {
  8848. for (int i = env->line_no + 1; i <= env->prev_line; ++i) {
  8849. _redraw_line_char(i,1);
  8850. }
  8851. env->prev_line = env->line_no;
  8852. }
  8853. }
  8854. } else if (env->mode == MODE_COL_SELECTION) {
  8855. place_cursor_actual();
  8856. int key = bim_getkey(200);
  8857. if (key == KEY_TIMEOUT) continue;
  8858. if (handle_nav_buffer(key)) {
  8859. if (!handle_action(COL_SELECTION_MAP, key))
  8860. if (!handle_action(NAVIGATION_MAP, key))
  8861. handle_action(ESCAPE_MAP, key);
  8862. }
  8863. reset_nav_buffer(key);
  8864. if (env->mode == MODE_COL_SELECTION) {
  8865. _redraw_line_col(env->line_no, 0);
  8866. /* Properly mark everything in the span we just moved through */
  8867. if (env->prev_line < env->line_no) {
  8868. for (int i = env->prev_line; i < env->line_no; ++i) {
  8869. _redraw_line_col(i,0);
  8870. }
  8871. env->prev_line = env->line_no;
  8872. } else if (env->prev_line > env->line_no) {
  8873. for (int i = env->line_no + 1; i <= env->prev_line; ++i) {
  8874. _redraw_line_col(i,0);
  8875. }
  8876. env->prev_line = env->line_no;
  8877. }
  8878. redraw_commandline();
  8879. }
  8880. } else if (env->mode == MODE_COL_INSERT) {
  8881. int key = bim_getkey(refresh ? 10 : 200);
  8882. if (key == KEY_TIMEOUT) {
  8883. if (refresh) {
  8884. redraw_commandline();
  8885. redraw_text();
  8886. }
  8887. refresh = 0;
  8888. } else if (handle_action(COL_INSERT_MAP, key)) {
  8889. /* pass */
  8890. } else if (key < KEY_ESCAPE) {
  8891. insert_char_at_column(key);
  8892. refresh = 1;
  8893. }
  8894. } if (env->mode == MODE_DIRECTORY_BROWSE) {
  8895. place_cursor_actual();
  8896. int key = bim_getkey(200);
  8897. if (handle_nav_buffer(key)) {
  8898. if (!handle_action(DIRECTORY_BROWSE_MAP, key))
  8899. if (!handle_action(NAVIGATION_MAP, key))
  8900. handle_action(ESCAPE_MAP, key);
  8901. }
  8902. reset_nav_buffer(key);
  8903. }
  8904. }
  8905. }
  8906. /**
  8907. * Show help text for -?
  8908. */
  8909. static void show_usage(char * argv[]) {
  8910. #define _s "\033[3m"
  8911. #define _e "\033[0m\n"
  8912. printf(
  8913. "bim - Text editor\n"
  8914. "\n"
  8915. "usage: %s [options] [file]\n"
  8916. " %s [options] -- -\n"
  8917. "\n"
  8918. " -R " _s "open initial buffer read-only" _e
  8919. " -O " _s "set various options:" _e
  8920. " noscroll " _s "disable terminal scrolling" _e
  8921. " noaltscreen " _s "disable alternate screen buffer" _e
  8922. " nomouse " _s "disable mouse support" _e
  8923. " nounicode " _s "disable unicode display" _e
  8924. " nobright " _s "disable bright next" _e
  8925. " nohideshow " _s "disable togglging cursor visibility" _e
  8926. " nosyntax " _s "disable syntax highlighting on load" _e
  8927. " notitle " _s "disable title-setting escapes" _e
  8928. " history " _s "enable experimental undo/redo" _e
  8929. " -c,-C " _s "print file to stdout with syntax highlighting" _e
  8930. " " _s "-C includes line numbers, -c does not" _e
  8931. " -u " _s "override bimrc file" _e
  8932. " -? " _s "show this help text" _e
  8933. "\n"
  8934. "Long options:\n"
  8935. " --help " _s "show this help text" _e
  8936. " --version " _s "show version information and available plugins" _e
  8937. " --dump-mappings " _s "dump markdown description of key mappings" _e
  8938. " --dump-commands " _s "dump markdown description of all commands" _e
  8939. " --dump-config " _s "dump key mappings as a bimscript" _e
  8940. " --html FILE " _s "convert FILE to syntax-highlighted HTML" _e
  8941. "\n", argv[0], argv[0]);
  8942. #undef _e
  8943. #undef _s
  8944. }
  8945. void free_function(struct bim_function * func) {
  8946. do {
  8947. struct bim_function * next = func->next;
  8948. free(func->command);
  8949. free(func);
  8950. func = next;
  8951. } while (func);
  8952. }
  8953. int run_function(char * name) {
  8954. for (int i = 0; i < flex_user_functions_count; ++i) {
  8955. if (user_functions[i] && !strcmp(user_functions[i]->command, name)) {
  8956. /* Execute function */
  8957. struct bim_function * this = user_functions[i]->next;
  8958. while (this) {
  8959. char * tmp = strdup(this->command);
  8960. int result = process_command(tmp);
  8961. free(tmp);
  8962. if (result != 0) {
  8963. return result;
  8964. }
  8965. this = this->next;
  8966. }
  8967. return 0;
  8968. }
  8969. }
  8970. return -1;
  8971. }
  8972. int has_function(char * name) {
  8973. for (int i = 0; i < flex_user_functions_count; ++i) {
  8974. if (user_functions[i] && !strcmp(user_functions[i]->command, name)) {
  8975. return 1;
  8976. }
  8977. }
  8978. return 0;
  8979. }
  8980. BIM_COMMAND(call,"call","Call a function") {
  8981. if (argc < 2) {
  8982. render_error("Expected function name");
  8983. return 1;
  8984. }
  8985. int result = run_function(argv[1]);
  8986. if (result == -1) {
  8987. render_error("Undefined function: %s", argv[1]);
  8988. return 1;
  8989. }
  8990. return result;