bim.c 360 KB

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