vim_spec.lua 186 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local Screen = require('test.functional.ui.screen')
  4. local uv = vim.uv
  5. local fmt = string.format
  6. local dedent = t.dedent
  7. local assert_alive = n.assert_alive
  8. local NIL = vim.NIL
  9. local clear, eq, neq = n.clear, t.eq, t.neq
  10. local command = n.command
  11. local command_output = n.api.nvim_command_output
  12. local exec = n.exec
  13. local exec_capture = n.exec_capture
  14. local eval = n.eval
  15. local expect = n.expect
  16. local fn = n.fn
  17. local api = n.api
  18. local matches = t.matches
  19. local pesc = vim.pesc
  20. local mkdir_p = n.mkdir_p
  21. local ok, nvim_async, feed = t.ok, n.nvim_async, n.feed
  22. local async_meths = n.async_meths
  23. local is_os = t.is_os
  24. local parse_context = n.parse_context
  25. local request = n.request
  26. local rmdir = n.rmdir
  27. local source = n.source
  28. local next_msg = n.next_msg
  29. local tmpname = t.tmpname
  30. local write_file = t.write_file
  31. local exec_lua = n.exec_lua
  32. local exc_exec = n.exc_exec
  33. local insert = n.insert
  34. local skip = t.skip
  35. local pcall_err = t.pcall_err
  36. local format_string = require('test.format_string').format_string
  37. local intchar2lua = t.intchar2lua
  38. local mergedicts_copy = t.mergedicts_copy
  39. local endswith = vim.endswith
  40. describe('API', function()
  41. before_each(clear)
  42. it('validates requests', function()
  43. -- RPC
  44. matches('Invalid method: bogus$', pcall_err(request, 'bogus'))
  45. matches('Invalid method: … の り 。…$', pcall_err(request, '… の り 。…'))
  46. matches('Invalid method: <empty>$', pcall_err(request, ''))
  47. -- Non-RPC: rpcrequest(v:servername) uses internal channel.
  48. matches(
  49. 'Invalid method: … の り 。…$',
  50. pcall_err(
  51. request,
  52. 'nvim_eval',
  53. [=[rpcrequest(sockconnect('pipe', v:servername, {'rpc':1}), '… の り 。…')]=]
  54. )
  55. )
  56. matches(
  57. 'Invalid method: bogus$',
  58. pcall_err(
  59. request,
  60. 'nvim_eval',
  61. [=[rpcrequest(sockconnect('pipe', v:servername, {'rpc':1}), 'bogus')]=]
  62. )
  63. )
  64. -- XXX: This must be the last one, else next one will fail:
  65. -- "Packer instance already working. Use another Packer ..."
  66. matches("can't serialize object of type .$", pcall_err(request, nil))
  67. end)
  68. it('handles errors in async requests', function()
  69. local error_types = api.nvim_get_api_info()[2].error_types
  70. nvim_async('bogus')
  71. eq({
  72. 'notification',
  73. 'nvim_error_event',
  74. { error_types.Exception.id, 'Invalid method: bogus' },
  75. }, next_msg())
  76. -- error didn't close channel.
  77. assert_alive()
  78. end)
  79. it('failed async request emits nvim_error_event', function()
  80. local error_types = api.nvim_get_api_info()[2].error_types
  81. async_meths.nvim_command('bogus')
  82. eq({
  83. 'notification',
  84. 'nvim_error_event',
  85. { error_types.Exception.id, 'Vim:E492: Not an editor command: bogus' },
  86. }, next_msg())
  87. -- error didn't close channel.
  88. assert_alive()
  89. end)
  90. it('input is processed first when followed immediately by non-fast events', function()
  91. api.nvim_set_current_line('ab')
  92. async_meths.nvim_input('x')
  93. async_meths.nvim_exec_lua('_G.res1 = vim.api.nvim_get_current_line()', {})
  94. async_meths.nvim_exec_lua('_G.res2 = vim.api.nvim_get_current_line()', {})
  95. eq({ 'b', 'b' }, exec_lua('return { _G.res1, _G.res2 }'))
  96. end)
  97. it('does not set CA_COMMAND_BUSY #7254', function()
  98. command('split')
  99. command('autocmd WinEnter * startinsert')
  100. command('wincmd w')
  101. eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
  102. end)
  103. describe('nvim_exec2', function()
  104. it('always returns table', function()
  105. -- In built version this results into `vim.empty_dict()`
  106. eq({}, api.nvim_exec2('echo "Hello"', {}))
  107. eq({}, api.nvim_exec2('echo "Hello"', { output = false }))
  108. eq({ output = 'Hello' }, api.nvim_exec2('echo "Hello"', { output = true }))
  109. end)
  110. it('default options', function()
  111. -- Should be equivalent to { output = false }
  112. api.nvim_exec2("let x0 = 'a'", {})
  113. eq('a', api.nvim_get_var('x0'))
  114. end)
  115. it('one-line input', function()
  116. api.nvim_exec2("let x1 = 'a'", { output = false })
  117. eq('a', api.nvim_get_var('x1'))
  118. end)
  119. it(':verbose set {option}?', function()
  120. api.nvim_exec2('set nowrap', { output = false })
  121. eq(
  122. { output = 'nowrap\n\tLast set from anonymous :source' },
  123. api.nvim_exec2('verbose set wrap?', { output = true })
  124. )
  125. -- Using script var to force creation of a script item
  126. api.nvim_exec2(
  127. [[
  128. let s:a = 1
  129. set nowrap
  130. ]],
  131. { output = false }
  132. )
  133. eq(
  134. { output = 'nowrap\n\tLast set from anonymous :source (script id 1)' },
  135. api.nvim_exec2('verbose set wrap?', { output = true })
  136. )
  137. end)
  138. it('multiline input', function()
  139. -- Heredoc + empty lines.
  140. api.nvim_exec2("let x2 = 'a'\n", { output = false })
  141. eq('a', api.nvim_get_var('x2'))
  142. api.nvim_exec2('lua <<EOF\n\n\n\ny=3\n\n\nEOF', { output = false })
  143. eq(3, api.nvim_eval("luaeval('y')"))
  144. eq({}, api.nvim_exec2('lua <<EOF\ny=3\nEOF', { output = false }))
  145. eq(3, api.nvim_eval("luaeval('y')"))
  146. -- Multiple statements
  147. api.nvim_exec2('let x1=1\nlet x2=2\nlet x3=3\n', { output = false })
  148. eq(1, api.nvim_eval('x1'))
  149. eq(2, api.nvim_eval('x2'))
  150. eq(3, api.nvim_eval('x3'))
  151. -- Functions
  152. api.nvim_exec2('function Foo()\ncall setline(1,["xxx"])\nendfunction', { output = false })
  153. eq('', api.nvim_get_current_line())
  154. api.nvim_exec2('call Foo()', { output = false })
  155. eq('xxx', api.nvim_get_current_line())
  156. -- Autocmds
  157. api.nvim_exec2('autocmd BufAdd * :let x1 = "Hello"', { output = false })
  158. command('new foo')
  159. eq('Hello', request('nvim_eval', 'g:x1'))
  160. -- Line continuations
  161. api.nvim_exec2(
  162. [[
  163. let abc = #{
  164. \ a: 1,
  165. "\ b: 2,
  166. \ c: 3
  167. \ }]],
  168. { output = false }
  169. )
  170. eq({ a = 1, c = 3 }, request('nvim_eval', 'g:abc'))
  171. -- try no spaces before continuations to catch off-by-one error
  172. api.nvim_exec2('let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', { output = false })
  173. eq({ a = 98 }, request('nvim_eval', 'g:ab'))
  174. -- Script scope (s:)
  175. eq(
  176. { output = 'ahoy! script-scoped varrrrr' },
  177. api.nvim_exec2(
  178. [[
  179. let s:pirate = 'script-scoped varrrrr'
  180. function! s:avast_ye_hades(s) abort
  181. return a:s .. ' ' .. s:pirate
  182. endfunction
  183. echo <sid>avast_ye_hades('ahoy!')
  184. ]],
  185. { output = true }
  186. )
  187. )
  188. eq(
  189. { output = "{'output': 'ahoy! script-scoped varrrrr'}" },
  190. api.nvim_exec2(
  191. [[
  192. let s:pirate = 'script-scoped varrrrr'
  193. function! Avast_ye_hades(s) abort
  194. return a:s .. ' ' .. s:pirate
  195. endfunction
  196. echo nvim_exec2('echo Avast_ye_hades(''ahoy!'')', {'output': v:true})
  197. ]],
  198. { output = true }
  199. )
  200. )
  201. matches(
  202. 'Vim%(echo%):E121: Undefined variable: s:pirate$',
  203. pcall_err(
  204. request,
  205. 'nvim_exec2',
  206. [[
  207. let s:pirate = 'script-scoped varrrrr'
  208. call nvim_exec2('echo s:pirate', {'output': v:true})
  209. ]],
  210. { output = false }
  211. )
  212. )
  213. -- Script items are created only on script var access
  214. eq(
  215. { output = '1\n0' },
  216. api.nvim_exec2(
  217. [[
  218. echo expand("<SID>")->empty()
  219. let s:a = 123
  220. echo expand("<SID>")->empty()
  221. ]],
  222. { output = true }
  223. )
  224. )
  225. eq(
  226. { output = '1\n0' },
  227. api.nvim_exec2(
  228. [[
  229. echo expand("<SID>")->empty()
  230. function s:a() abort
  231. endfunction
  232. echo expand("<SID>")->empty()
  233. ]],
  234. { output = true }
  235. )
  236. )
  237. end)
  238. it('non-ASCII input', function()
  239. api.nvim_exec2(
  240. [=[
  241. new
  242. exe "normal! i ax \n Ax "
  243. :%s/ax/--a1234--/g | :%s/Ax/--A1234--/g
  244. ]=],
  245. { output = false }
  246. )
  247. command('1')
  248. eq(' --a1234-- ', api.nvim_get_current_line())
  249. command('2')
  250. eq(' --A1234-- ', api.nvim_get_current_line())
  251. api.nvim_exec2(
  252. [[
  253. new
  254. call setline(1,['xxx'])
  255. call feedkeys('r')
  256. call feedkeys('ñ', 'xt')
  257. ]],
  258. { output = false }
  259. )
  260. eq('ñxx', api.nvim_get_current_line())
  261. end)
  262. it('execution error', function()
  263. eq(
  264. 'nvim_exec2(): Vim:E492: Not an editor command: bogus_command',
  265. pcall_err(request, 'nvim_exec2', 'bogus_command', {})
  266. )
  267. eq('', api.nvim_eval('v:errmsg')) -- v:errmsg was not updated.
  268. eq('', eval('v:exception'))
  269. eq(
  270. 'nvim_exec2(): Vim(buffer):E86: Buffer 23487 does not exist',
  271. pcall_err(request, 'nvim_exec2', 'buffer 23487', {})
  272. )
  273. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  274. eq('', eval('v:exception'))
  275. end)
  276. it('recursion', function()
  277. local fname = tmpname()
  278. write_file(fname, 'let x1 = "set from :source file"\n')
  279. -- nvim_exec2
  280. -- :source
  281. -- nvim_exec2
  282. request('nvim_exec2', [[
  283. let x2 = substitute('foo','o','X','g')
  284. let x4 = 'should be overwritten'
  285. call nvim_exec2("source ]] .. fname .. [[\nlet x3 = substitute('foo','foo','set by recursive nvim_exec2','g')\nlet x5='overwritten'\nlet x4=x5\n", {'output': v:false})
  286. ]], { output = false })
  287. eq('set from :source file', request('nvim_get_var', 'x1'))
  288. eq('fXX', request('nvim_get_var', 'x2'))
  289. eq('set by recursive nvim_exec2', request('nvim_get_var', 'x3'))
  290. eq('overwritten', request('nvim_get_var', 'x4'))
  291. eq('overwritten', request('nvim_get_var', 'x5'))
  292. os.remove(fname)
  293. end)
  294. it('traceback', function()
  295. local fname = tmpname()
  296. write_file(fname, 'echo "hello"\n')
  297. local sourcing_fname = tmpname()
  298. write_file(sourcing_fname, 'call nvim_exec2("source ' .. fname .. '", {"output": v:false})\n')
  299. api.nvim_exec2('set verbose=2', { output = false })
  300. local traceback_output = dedent([[
  301. line 0: sourcing "%s"
  302. line 0: sourcing "%s"
  303. hello
  304. finished sourcing %s
  305. continuing in nvim_exec2() called at %s:1
  306. finished sourcing %s
  307. continuing in nvim_exec2() called at nvim_exec2():0]]):format(
  308. sourcing_fname,
  309. fname,
  310. fname,
  311. sourcing_fname,
  312. sourcing_fname
  313. )
  314. eq(
  315. { output = traceback_output },
  316. api.nvim_exec2(
  317. 'call nvim_exec2("source ' .. sourcing_fname .. '", {"output": v:false})',
  318. { output = true }
  319. )
  320. )
  321. os.remove(fname)
  322. os.remove(sourcing_fname)
  323. end)
  324. it('returns output', function()
  325. eq(
  326. { output = 'this is spinal tap' },
  327. api.nvim_exec2('lua <<EOF\n\n\nprint("this is spinal tap")\n\n\nEOF', { output = true })
  328. )
  329. eq({ output = '' }, api.nvim_exec2('echo', { output = true }))
  330. eq({ output = 'foo 42' }, api.nvim_exec2('echo "foo" 42', { output = true }))
  331. end)
  332. it('displays messages when opts.output=false', function()
  333. local screen = Screen.new(40, 8)
  334. api.nvim_exec2("echo 'hello'", { output = false })
  335. screen:expect {
  336. grid = [[
  337. ^ |
  338. {1:~ }|*6
  339. hello |
  340. ]],
  341. }
  342. end)
  343. it("doesn't display messages when output=true", function()
  344. local screen = Screen.new(40, 6)
  345. api.nvim_exec2("echo 'hello'", { output = true })
  346. screen:expect {
  347. grid = [[
  348. ^ |
  349. {1:~ }|*4
  350. |
  351. ]],
  352. }
  353. exec([[
  354. func Print()
  355. call nvim_exec2('echo "hello"', { 'output': v:true })
  356. endfunc
  357. ]])
  358. feed([[:echon 1 | call Print() | echon 5<CR>]])
  359. screen:expect {
  360. grid = [[
  361. ^ |
  362. {1:~ }|*4
  363. 15 |
  364. ]],
  365. }
  366. end)
  367. it('errors properly when command too recursive', function()
  368. exec_lua([[
  369. _G.success = false
  370. vim.api.nvim_create_user_command('Test', function()
  371. vim.api.nvim_exec2('Test', {})
  372. _G.success = true
  373. end, {})
  374. ]])
  375. pcall_err(command, 'Test')
  376. assert_alive()
  377. eq(false, exec_lua('return _G.success'))
  378. end)
  379. end)
  380. describe('nvim_command', function()
  381. it('works', function()
  382. local fname = tmpname()
  383. command('new')
  384. command('edit ' .. fname)
  385. command('normal itesting\napi')
  386. command('w')
  387. local f = assert(io.open(fname))
  388. if is_os('win') then
  389. eq('testing\r\napi\r\n', f:read('*a'))
  390. else
  391. eq('testing\napi\n', f:read('*a'))
  392. end
  393. f:close()
  394. os.remove(fname)
  395. end)
  396. it('Vimscript validation error: fails with specific error', function()
  397. local status, rv = pcall(command, 'bogus_command')
  398. eq(false, status) -- nvim_command() failed.
  399. eq('E492:', string.match(rv, 'E%d*:')) -- Vimscript error was returned.
  400. eq('', api.nvim_eval('v:errmsg')) -- v:errmsg was not updated.
  401. eq('', eval('v:exception'))
  402. end)
  403. it('Vimscript execution error: fails with specific error', function()
  404. local status, rv = pcall(command, 'buffer 23487')
  405. eq(false, status) -- nvim_command() failed.
  406. eq('E86: Buffer 23487 does not exist', string.match(rv, 'E%d*:.*'))
  407. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  408. eq('', eval('v:exception'))
  409. end)
  410. it('gives E493 instead of prompting on backwards range', function()
  411. command('split')
  412. eq(
  413. 'Vim(windo):E493: Backwards range given: 2,1windo echo',
  414. pcall_err(command, '2,1windo echo')
  415. )
  416. end)
  417. end)
  418. describe('nvim_command_output', function()
  419. it('does not induce hit-enter prompt', function()
  420. api.nvim_ui_attach(80, 20, {})
  421. -- Induce a hit-enter prompt use nvim_input (non-blocking).
  422. command('set cmdheight=1')
  423. api.nvim_input([[:echo "hi\nhi2"<CR>]])
  424. -- Verify hit-enter prompt.
  425. eq({ mode = 'r', blocking = true }, api.nvim_get_mode())
  426. api.nvim_input([[<C-c>]])
  427. -- Verify NO hit-enter prompt.
  428. command_output([[echo "hi\nhi2"]])
  429. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  430. end)
  431. it('captures command output', function()
  432. eq('this is\nspinal tap', command_output([[echo "this is\nspinal tap"]]))
  433. eq('no line ending!', command_output([[echon "no line ending!"]]))
  434. end)
  435. it('captures empty command output', function()
  436. eq('', command_output('echo'))
  437. end)
  438. it('captures single-char command output', function()
  439. eq('x', command_output('echo "x"'))
  440. end)
  441. it('captures multiple commands', function()
  442. eq('foo\n 1 %a "[No Name]" line 1', command_output('echo "foo" | ls'))
  443. end)
  444. it('captures nested execute()', function()
  445. eq(
  446. '\nnested1\nnested2\n 1 %a "[No Name]" line 1',
  447. command_output([[echo execute('echo "nested1\nnested2"') | ls]])
  448. )
  449. end)
  450. it('captures nested nvim_command_output()', function()
  451. eq(
  452. 'nested1\nnested2\n 1 %a "[No Name]" line 1',
  453. command_output([[echo nvim_command_output('echo "nested1\nnested2"') | ls]])
  454. )
  455. end)
  456. it('returns shell |:!| output', function()
  457. local win_lf = is_os('win') and '\r' or ''
  458. eq(':!echo foo\r\n\nfoo' .. win_lf .. '\n', command_output([[!echo foo]]))
  459. end)
  460. it('Vimscript validation error: fails with specific error', function()
  461. local status, rv = pcall(command_output, 'bogus commannnd')
  462. eq(false, status) -- nvim_command_output() failed.
  463. eq('E492: Not an editor command: bogus commannnd', string.match(rv, 'E%d*:.*'))
  464. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  465. -- Verify NO hit-enter prompt.
  466. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  467. end)
  468. it('Vimscript execution error: fails with specific error', function()
  469. local status, rv = pcall(command_output, 'buffer 42')
  470. eq(false, status) -- nvim_command_output() failed.
  471. eq('E86: Buffer 42 does not exist', string.match(rv, 'E%d*:.*'))
  472. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  473. -- Verify NO hit-enter prompt.
  474. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  475. end)
  476. it('does not cause heap buffer overflow with large output', function()
  477. eq(eval('string(range(1000000))'), command_output('echo range(1000000)'))
  478. end)
  479. end)
  480. describe('nvim_eval', function()
  481. it('works', function()
  482. command('let g:v1 = "a"')
  483. command('let g:v2 = [1, 2, {"v3": 3}]')
  484. eq({ v1 = 'a', v2 = { 1, 2, { v3 = 3 } } }, api.nvim_eval('g:'))
  485. end)
  486. it('handles NULL-initialized strings correctly', function()
  487. eq(1, api.nvim_eval("matcharg(1) == ['', '']"))
  488. eq({ '', '' }, api.nvim_eval('matcharg(1)'))
  489. end)
  490. it('works under deprecated name', function()
  491. eq(2, request('vim_eval', '1+1'))
  492. end)
  493. it('Vimscript error: returns error details, does NOT update v:errmsg', function()
  494. eq('Vim:E121: Undefined variable: bogus', pcall_err(request, 'nvim_eval', 'bogus expression'))
  495. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  496. end)
  497. it('can return Lua function to Lua code', function()
  498. eq(
  499. [["a string with \"double quotes\" and 'single quotes'"]],
  500. exec_lua([=[
  501. local fun = vim.api.nvim_eval([[luaeval('string.format')]])
  502. return fun('%q', [[a string with "double quotes" and 'single quotes']])
  503. ]=])
  504. )
  505. end)
  506. end)
  507. describe('nvim_call_function', function()
  508. it('works', function()
  509. api.nvim_call_function('setqflist', { { { filename = 'something', lnum = 17 } }, 'r' })
  510. eq(17, api.nvim_call_function('getqflist', {})[1].lnum)
  511. eq(17, api.nvim_call_function('eval', { 17 }))
  512. eq('foo', api.nvim_call_function('simplify', { 'this/./is//redundant/../../../foo' }))
  513. end)
  514. it('Vimscript validation error: returns specific error, does NOT update v:errmsg', function()
  515. eq(
  516. 'Vim:E117: Unknown function: bogus function',
  517. pcall_err(request, 'nvim_call_function', 'bogus function', { 'arg1' })
  518. )
  519. eq(
  520. 'Vim:E119: Not enough arguments for function: atan',
  521. pcall_err(request, 'nvim_call_function', 'atan', {})
  522. )
  523. eq('', eval('v:exception'))
  524. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  525. end)
  526. it('Vimscript error: returns error details, does NOT update v:errmsg', function()
  527. eq(
  528. 'Vim:E808: Number or Float required',
  529. pcall_err(request, 'nvim_call_function', 'atan', { 'foo' })
  530. )
  531. eq(
  532. 'Vim:Invalid channel stream "xxx"',
  533. pcall_err(request, 'nvim_call_function', 'chanclose', { 999, 'xxx' })
  534. )
  535. eq(
  536. 'Vim:E900: Invalid channel id',
  537. pcall_err(request, 'nvim_call_function', 'chansend', { 999, 'foo' })
  538. )
  539. eq('', eval('v:exception'))
  540. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  541. end)
  542. it('Vimscript exception: returns exception details, does NOT update v:errmsg', function()
  543. source([[
  544. function! Foo() abort
  545. throw 'wtf'
  546. endfunction
  547. ]])
  548. eq('function Foo, line 1: wtf', pcall_err(request, 'nvim_call_function', 'Foo', {}))
  549. eq('', eval('v:exception'))
  550. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  551. end)
  552. it('validation', function()
  553. -- stylua: ignore
  554. local too_many_args = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' }
  555. source([[
  556. function! Foo(...) abort
  557. echo a:000
  558. endfunction
  559. ]])
  560. -- E740
  561. eq(
  562. 'Function called with too many arguments',
  563. pcall_err(request, 'nvim_call_function', 'Foo', too_many_args)
  564. )
  565. end)
  566. it('can return Lua function to Lua code', function()
  567. eq(
  568. [["a string with \"double quotes\" and 'single quotes'"]],
  569. exec_lua([=[
  570. local fun = vim.api.nvim_call_function('luaeval', { 'string.format' })
  571. return fun('%q', [[a string with "double quotes" and 'single quotes']])
  572. ]=])
  573. )
  574. end)
  575. end)
  576. describe('nvim_call_dict_function', function()
  577. it('invokes Vimscript dict function', function()
  578. source([[
  579. function! F(name) dict
  580. return self.greeting.', '.a:name.'!'
  581. endfunction
  582. let g:test_dict_fn = { 'greeting':'Hello', 'F':function('F') }
  583. let g:test_dict_fn2 = { 'greeting':'Hi' }
  584. function g:test_dict_fn2.F2(name)
  585. return self.greeting.', '.a:name.' ...'
  586. endfunction
  587. ]])
  588. -- :help Dictionary-function
  589. eq('Hello, World!', api.nvim_call_dict_function('g:test_dict_fn', 'F', { 'World' }))
  590. -- Funcref is sent as NIL over RPC.
  591. eq({ greeting = 'Hello', F = NIL }, api.nvim_get_var('test_dict_fn'))
  592. -- :help numbered-function
  593. eq('Hi, Moon ...', api.nvim_call_dict_function('g:test_dict_fn2', 'F2', { 'Moon' }))
  594. -- Funcref is sent as NIL over RPC.
  595. eq({ greeting = 'Hi', F2 = NIL }, api.nvim_get_var('test_dict_fn2'))
  596. -- Function specified via RPC dict.
  597. source('function! G() dict\n return "@".(self.result)."@"\nendfunction')
  598. eq('@it works@', api.nvim_call_dict_function({ result = 'it works', G = 'G' }, 'G', {}))
  599. end)
  600. it('validation', function()
  601. command('let g:d={"baz":"zub","meep":[]}')
  602. eq(
  603. 'Not found: bogus',
  604. pcall_err(request, 'nvim_call_dict_function', 'g:d', 'bogus', { 1, 2 })
  605. )
  606. eq(
  607. 'Not a function: baz',
  608. pcall_err(request, 'nvim_call_dict_function', 'g:d', 'baz', { 1, 2 })
  609. )
  610. eq(
  611. 'Not a function: meep',
  612. pcall_err(request, 'nvim_call_dict_function', 'g:d', 'meep', { 1, 2 })
  613. )
  614. eq(
  615. 'Vim:E117: Unknown function: f',
  616. pcall_err(request, 'nvim_call_dict_function', { f = '' }, 'f', { 1, 2 })
  617. )
  618. eq(
  619. 'Not a function: f',
  620. pcall_err(request, 'nvim_call_dict_function', "{ 'f': '' }", 'f', { 1, 2 })
  621. )
  622. eq(
  623. 'dict argument type must be String or Dict',
  624. pcall_err(request, 'nvim_call_dict_function', 42, 'f', { 1, 2 })
  625. )
  626. eq(
  627. 'Vim:E121: Undefined variable: foo',
  628. pcall_err(request, 'nvim_call_dict_function', 'foo', 'f', { 1, 2 })
  629. )
  630. eq('dict not found', pcall_err(request, 'nvim_call_dict_function', '42', 'f', { 1, 2 }))
  631. eq(
  632. 'Invalid (empty) function name',
  633. pcall_err(request, 'nvim_call_dict_function', "{ 'f': '' }", '', { 1, 2 })
  634. )
  635. end)
  636. end)
  637. describe('nvim_set_current_dir', function()
  638. local start_dir
  639. before_each(function()
  640. fn.mkdir('Xtestdir')
  641. start_dir = fn.getcwd()
  642. end)
  643. after_each(function()
  644. n.rmdir('Xtestdir')
  645. end)
  646. it('works', function()
  647. api.nvim_set_current_dir('Xtestdir')
  648. eq(start_dir .. n.get_pathsep() .. 'Xtestdir', fn.getcwd())
  649. end)
  650. it('sets previous directory', function()
  651. api.nvim_set_current_dir('Xtestdir')
  652. command('cd -')
  653. eq(start_dir, fn.getcwd())
  654. end)
  655. end)
  656. describe('nvim_exec_lua', function()
  657. it('works', function()
  658. api.nvim_exec_lua('vim.api.nvim_set_var("test", 3)', {})
  659. eq(3, api.nvim_get_var('test'))
  660. eq(17, api.nvim_exec_lua('a, b = ...\nreturn a + b', { 10, 7 }))
  661. eq(NIL, api.nvim_exec_lua('function xx(a,b)\nreturn a..b\nend', {}))
  662. eq('xy', api.nvim_exec_lua('return xx(...)', { 'x', 'y' }))
  663. -- Deprecated name: nvim_execute_lua.
  664. eq('xy', api.nvim_execute_lua('return xx(...)', { 'x', 'y' }))
  665. end)
  666. it('reports errors', function()
  667. eq(
  668. [[Error loading lua: [string "<nvim>"]:0: '=' expected near '+']],
  669. pcall_err(api.nvim_exec_lua, 'a+*b', {})
  670. )
  671. eq(
  672. [[Error loading lua: [string "<nvim>"]:0: unexpected symbol near '1']],
  673. pcall_err(api.nvim_exec_lua, '1+2', {})
  674. )
  675. eq(
  676. [[Error loading lua: [string "<nvim>"]:0: unexpected symbol]],
  677. pcall_err(api.nvim_exec_lua, 'aa=bb\0', {})
  678. )
  679. eq(
  680. [[attempt to call global 'bork' (a nil value)]],
  681. pcall_err(api.nvim_exec_lua, 'bork()', {})
  682. )
  683. eq('did\nthe\nfail', pcall_err(api.nvim_exec_lua, 'error("did\\nthe\\nfail")', {}))
  684. end)
  685. it('uses native float values', function()
  686. eq(2.5, api.nvim_exec_lua('return select(1, ...)', { 2.5 }))
  687. eq('2.5', api.nvim_exec_lua('return vim.inspect(...)', { 2.5 }))
  688. -- "special" float values are still accepted as return values.
  689. eq(2.5, api.nvim_exec_lua("return vim.api.nvim_eval('2.5')", {}))
  690. eq(
  691. '{\n [false] = 2.5,\n [true] = 3\n}',
  692. api.nvim_exec_lua("return vim.inspect(vim.api.nvim_eval('2.5'))", {})
  693. )
  694. end)
  695. end)
  696. describe('nvim_input', function()
  697. it('Vimscript error: does NOT fail, updates v:errmsg', function()
  698. local status, _ = pcall(api.nvim_input, ':call bogus_fn()<CR>')
  699. local v_errnum = string.match(api.nvim_eval('v:errmsg'), 'E%d*:')
  700. eq(true, status) -- nvim_input() did not fail.
  701. eq('E117:', v_errnum) -- v:errmsg was updated.
  702. end)
  703. it('does not crash even if trans_special result is largest #11788, #12287', function()
  704. command("call nvim_input('<M-'.nr2char(0x40000000).'>')")
  705. eq(1, eval('1'))
  706. end)
  707. end)
  708. describe('nvim_paste', function()
  709. it('validation', function()
  710. eq("Invalid 'phase': -2", pcall_err(request, 'nvim_paste', 'foo', true, -2))
  711. eq("Invalid 'phase': 4", pcall_err(request, 'nvim_paste', 'foo', true, 4))
  712. end)
  713. local function run_streamed_paste_tests()
  714. it('stream: multiple chunks form one undo-block', function()
  715. api.nvim_paste('1/chunk 1 (start)\n', true, 1)
  716. api.nvim_paste('1/chunk 2 (end)\n', true, 3)
  717. local expected1 = [[
  718. 1/chunk 1 (start)
  719. 1/chunk 2 (end)
  720. ]]
  721. expect(expected1)
  722. api.nvim_paste('2/chunk 1 (start)\n', true, 1)
  723. api.nvim_paste('2/chunk 2\n', true, 2)
  724. expect([[
  725. 1/chunk 1 (start)
  726. 1/chunk 2 (end)
  727. 2/chunk 1 (start)
  728. 2/chunk 2
  729. ]])
  730. api.nvim_paste('2/chunk 3\n', true, 2)
  731. api.nvim_paste('2/chunk 4 (end)\n', true, 3)
  732. expect([[
  733. 1/chunk 1 (start)
  734. 1/chunk 2 (end)
  735. 2/chunk 1 (start)
  736. 2/chunk 2
  737. 2/chunk 3
  738. 2/chunk 4 (end)
  739. ]])
  740. feed('u') -- Undo.
  741. expect(expected1)
  742. end)
  743. it('stream: Insert mode', function()
  744. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  745. feed('afoo<Esc>u')
  746. feed('i')
  747. api.nvim_paste('aaaaaa', false, 1)
  748. api.nvim_paste('bbbbbb', false, 2)
  749. api.nvim_paste('cccccc', false, 2)
  750. api.nvim_paste('dddddd', false, 3)
  751. expect('aaaaaabbbbbbccccccdddddd')
  752. feed('<Esc>u')
  753. expect('')
  754. end)
  755. describe('stream: Normal mode', function()
  756. describe('on empty line', function()
  757. before_each(function()
  758. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  759. feed('afoo<Esc>u')
  760. end)
  761. after_each(function()
  762. feed('u')
  763. expect('')
  764. end)
  765. it('pasting one line', function()
  766. api.nvim_paste('aaaaaa', false, 1)
  767. api.nvim_paste('bbbbbb', false, 2)
  768. api.nvim_paste('cccccc', false, 2)
  769. api.nvim_paste('dddddd', false, 3)
  770. expect('aaaaaabbbbbbccccccdddddd')
  771. end)
  772. it('pasting multiple lines', function()
  773. api.nvim_paste('aaaaaa\n', false, 1)
  774. api.nvim_paste('bbbbbb\n', false, 2)
  775. api.nvim_paste('cccccc\n', false, 2)
  776. api.nvim_paste('dddddd', false, 3)
  777. expect([[
  778. aaaaaa
  779. bbbbbb
  780. cccccc
  781. dddddd]])
  782. end)
  783. end)
  784. describe('not at the end of a line', function()
  785. before_each(function()
  786. feed('i||<Esc>')
  787. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  788. feed('afoo<Esc>u')
  789. feed('0')
  790. end)
  791. after_each(function()
  792. feed('u')
  793. expect('||')
  794. end)
  795. it('pasting one line', function()
  796. api.nvim_paste('aaaaaa', false, 1)
  797. api.nvim_paste('bbbbbb', false, 2)
  798. api.nvim_paste('cccccc', false, 2)
  799. api.nvim_paste('dddddd', false, 3)
  800. expect('|aaaaaabbbbbbccccccdddddd|')
  801. end)
  802. it('pasting multiple lines', function()
  803. api.nvim_paste('aaaaaa\n', false, 1)
  804. api.nvim_paste('bbbbbb\n', false, 2)
  805. api.nvim_paste('cccccc\n', false, 2)
  806. api.nvim_paste('dddddd', false, 3)
  807. expect([[
  808. |aaaaaa
  809. bbbbbb
  810. cccccc
  811. dddddd|]])
  812. end)
  813. end)
  814. describe('at the end of a line', function()
  815. before_each(function()
  816. feed('i||<Esc>')
  817. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  818. feed('afoo<Esc>u')
  819. feed('2|')
  820. end)
  821. after_each(function()
  822. feed('u')
  823. expect('||')
  824. end)
  825. it('pasting one line', function()
  826. api.nvim_paste('aaaaaa', false, 1)
  827. api.nvim_paste('bbbbbb', false, 2)
  828. api.nvim_paste('cccccc', false, 2)
  829. api.nvim_paste('dddddd', false, 3)
  830. expect('||aaaaaabbbbbbccccccdddddd')
  831. end)
  832. it('pasting multiple lines', function()
  833. api.nvim_paste('aaaaaa\n', false, 1)
  834. api.nvim_paste('bbbbbb\n', false, 2)
  835. api.nvim_paste('cccccc\n', false, 2)
  836. api.nvim_paste('dddddd', false, 3)
  837. expect([[
  838. ||aaaaaa
  839. bbbbbb
  840. cccccc
  841. dddddd]])
  842. end)
  843. end)
  844. end)
  845. describe('stream: Visual mode', function()
  846. describe('neither end at the end of a line', function()
  847. before_each(function()
  848. feed('i|xxx<CR>xxx|<Esc>')
  849. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  850. feed('afoo<Esc>u')
  851. feed('3|vhk')
  852. end)
  853. after_each(function()
  854. feed('u')
  855. expect([[
  856. |xxx
  857. xxx|]])
  858. end)
  859. it('with non-empty chunks', function()
  860. api.nvim_paste('aaaaaa', false, 1)
  861. api.nvim_paste('bbbbbb', false, 2)
  862. api.nvim_paste('cccccc', false, 2)
  863. api.nvim_paste('dddddd', false, 3)
  864. expect('|aaaaaabbbbbbccccccdddddd|')
  865. end)
  866. it('with empty first chunk', function()
  867. api.nvim_paste('', false, 1)
  868. api.nvim_paste('bbbbbb', false, 2)
  869. api.nvim_paste('cccccc', false, 2)
  870. api.nvim_paste('dddddd', false, 3)
  871. expect('|bbbbbbccccccdddddd|')
  872. end)
  873. it('with all chunks empty', function()
  874. api.nvim_paste('', false, 1)
  875. api.nvim_paste('', false, 2)
  876. api.nvim_paste('', false, 2)
  877. api.nvim_paste('', false, 3)
  878. expect('||')
  879. end)
  880. end)
  881. describe('cursor at the end of a line', function()
  882. before_each(function()
  883. feed('i||xxx<CR>xxx<Esc>')
  884. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  885. feed('afoo<Esc>u')
  886. feed('3|vko')
  887. end)
  888. after_each(function()
  889. feed('u')
  890. expect([[
  891. ||xxx
  892. xxx]])
  893. end)
  894. it('with non-empty chunks', function()
  895. api.nvim_paste('aaaaaa', false, 1)
  896. api.nvim_paste('bbbbbb', false, 2)
  897. api.nvim_paste('cccccc', false, 2)
  898. api.nvim_paste('dddddd', false, 3)
  899. expect('||aaaaaabbbbbbccccccdddddd')
  900. end)
  901. it('with empty first chunk', function()
  902. api.nvim_paste('', false, 1)
  903. api.nvim_paste('bbbbbb', false, 2)
  904. api.nvim_paste('cccccc', false, 2)
  905. api.nvim_paste('dddddd', false, 3)
  906. expect('||bbbbbbccccccdddddd')
  907. end)
  908. end)
  909. describe('other end at the end of a line', function()
  910. before_each(function()
  911. feed('i||xxx<CR>xxx<Esc>')
  912. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  913. feed('afoo<Esc>u')
  914. feed('3|vk')
  915. end)
  916. after_each(function()
  917. feed('u')
  918. expect([[
  919. ||xxx
  920. xxx]])
  921. end)
  922. it('with non-empty chunks', function()
  923. api.nvim_paste('aaaaaa', false, 1)
  924. api.nvim_paste('bbbbbb', false, 2)
  925. api.nvim_paste('cccccc', false, 2)
  926. api.nvim_paste('dddddd', false, 3)
  927. expect('||aaaaaabbbbbbccccccdddddd')
  928. end)
  929. it('with empty first chunk', function()
  930. api.nvim_paste('', false, 1)
  931. api.nvim_paste('bbbbbb', false, 2)
  932. api.nvim_paste('cccccc', false, 2)
  933. api.nvim_paste('dddddd', false, 3)
  934. expect('||bbbbbbccccccdddddd')
  935. end)
  936. end)
  937. end)
  938. describe('stream: linewise Visual mode', function()
  939. before_each(function()
  940. feed('i123456789<CR>987654321<CR>123456789<Esc>')
  941. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  942. feed('afoo<Esc>u')
  943. end)
  944. after_each(function()
  945. feed('u')
  946. expect([[
  947. 123456789
  948. 987654321
  949. 123456789]])
  950. end)
  951. describe('selecting the start of a file', function()
  952. before_each(function()
  953. feed('ggV')
  954. end)
  955. it('pasting text without final new line', function()
  956. api.nvim_paste('aaaaaa\n', false, 1)
  957. api.nvim_paste('bbbbbb\n', false, 2)
  958. api.nvim_paste('cccccc\n', false, 2)
  959. api.nvim_paste('dddddd', false, 3)
  960. expect([[
  961. aaaaaa
  962. bbbbbb
  963. cccccc
  964. dddddd987654321
  965. 123456789]])
  966. end)
  967. it('pasting text with final new line', function()
  968. api.nvim_paste('aaaaaa\n', false, 1)
  969. api.nvim_paste('bbbbbb\n', false, 2)
  970. api.nvim_paste('cccccc\n', false, 2)
  971. api.nvim_paste('dddddd\n', false, 3)
  972. expect([[
  973. aaaaaa
  974. bbbbbb
  975. cccccc
  976. dddddd
  977. 987654321
  978. 123456789]])
  979. end)
  980. end)
  981. describe('selecting the middle of a file', function()
  982. before_each(function()
  983. feed('2ggV')
  984. end)
  985. it('pasting text without final new line', function()
  986. api.nvim_paste('aaaaaa\n', false, 1)
  987. api.nvim_paste('bbbbbb\n', false, 2)
  988. api.nvim_paste('cccccc\n', false, 2)
  989. api.nvim_paste('dddddd', false, 3)
  990. expect([[
  991. 123456789
  992. aaaaaa
  993. bbbbbb
  994. cccccc
  995. dddddd123456789]])
  996. end)
  997. it('pasting text with final new line', function()
  998. api.nvim_paste('aaaaaa\n', false, 1)
  999. api.nvim_paste('bbbbbb\n', false, 2)
  1000. api.nvim_paste('cccccc\n', false, 2)
  1001. api.nvim_paste('dddddd\n', false, 3)
  1002. expect([[
  1003. 123456789
  1004. aaaaaa
  1005. bbbbbb
  1006. cccccc
  1007. dddddd
  1008. 123456789]])
  1009. end)
  1010. end)
  1011. describe('selecting the end of a file', function()
  1012. before_each(function()
  1013. feed('3ggV')
  1014. end)
  1015. it('pasting text without final new line', function()
  1016. api.nvim_paste('aaaaaa\n', false, 1)
  1017. api.nvim_paste('bbbbbb\n', false, 2)
  1018. api.nvim_paste('cccccc\n', false, 2)
  1019. api.nvim_paste('dddddd', false, 3)
  1020. expect([[
  1021. 123456789
  1022. 987654321
  1023. aaaaaa
  1024. bbbbbb
  1025. cccccc
  1026. dddddd]])
  1027. end)
  1028. it('pasting text with final new line', function()
  1029. api.nvim_paste('aaaaaa\n', false, 1)
  1030. api.nvim_paste('bbbbbb\n', false, 2)
  1031. api.nvim_paste('cccccc\n', false, 2)
  1032. api.nvim_paste('dddddd\n', false, 3)
  1033. expect([[
  1034. 123456789
  1035. 987654321
  1036. aaaaaa
  1037. bbbbbb
  1038. cccccc
  1039. dddddd
  1040. ]])
  1041. end)
  1042. end)
  1043. describe('selecting the whole file', function()
  1044. before_each(function()
  1045. feed('ggVG')
  1046. end)
  1047. it('pasting text without final new line', function()
  1048. api.nvim_paste('aaaaaa\n', false, 1)
  1049. api.nvim_paste('bbbbbb\n', false, 2)
  1050. api.nvim_paste('cccccc\n', false, 2)
  1051. api.nvim_paste('dddddd', false, 3)
  1052. expect([[
  1053. aaaaaa
  1054. bbbbbb
  1055. cccccc
  1056. dddddd]])
  1057. end)
  1058. it('pasting text with final new line', function()
  1059. api.nvim_paste('aaaaaa\n', false, 1)
  1060. api.nvim_paste('bbbbbb\n', false, 2)
  1061. api.nvim_paste('cccccc\n', false, 2)
  1062. api.nvim_paste('dddddd\n', false, 3)
  1063. expect([[
  1064. aaaaaa
  1065. bbbbbb
  1066. cccccc
  1067. dddddd
  1068. ]])
  1069. end)
  1070. end)
  1071. end)
  1072. end
  1073. describe('without virtualedit,', function()
  1074. run_streamed_paste_tests()
  1075. end)
  1076. describe('with virtualedit=onemore,', function()
  1077. before_each(function()
  1078. command('set virtualedit=onemore')
  1079. end)
  1080. run_streamed_paste_tests()
  1081. end)
  1082. it('non-streaming', function()
  1083. -- With final "\n".
  1084. api.nvim_paste('line 1\nline 2\nline 3\n', true, -1)
  1085. expect([[
  1086. line 1
  1087. line 2
  1088. line 3
  1089. ]])
  1090. eq({ 0, 4, 1, 0 }, fn.getpos('.')) -- Cursor follows the paste.
  1091. eq(false, api.nvim_get_option_value('paste', {}))
  1092. command('%delete _')
  1093. -- Without final "\n".
  1094. api.nvim_paste('line 1\nline 2\nline 3', true, -1)
  1095. expect([[
  1096. line 1
  1097. line 2
  1098. line 3]])
  1099. eq({ 0, 3, 6, 0 }, fn.getpos('.'))
  1100. command('%delete _')
  1101. -- CRLF #10872
  1102. api.nvim_paste('line 1\r\nline 2\r\nline 3\r\n', true, -1)
  1103. expect([[
  1104. line 1
  1105. line 2
  1106. line 3
  1107. ]])
  1108. eq({ 0, 4, 1, 0 }, fn.getpos('.'))
  1109. command('%delete _')
  1110. -- CRLF without final "\n".
  1111. api.nvim_paste('line 1\r\nline 2\r\nline 3\r', true, -1)
  1112. expect([[
  1113. line 1
  1114. line 2
  1115. line 3
  1116. ]])
  1117. eq({ 0, 4, 1, 0 }, fn.getpos('.'))
  1118. command('%delete _')
  1119. -- CRLF without final "\r\n".
  1120. api.nvim_paste('line 1\r\nline 2\r\nline 3', true, -1)
  1121. expect([[
  1122. line 1
  1123. line 2
  1124. line 3]])
  1125. eq({ 0, 3, 6, 0 }, fn.getpos('.'))
  1126. command('%delete _')
  1127. -- Various other junk.
  1128. api.nvim_paste('line 1\r\n\r\rline 2\nline 3\rline 4\r', true, -1)
  1129. expect('line 1\n\n\nline 2\nline 3\nline 4\n')
  1130. eq({ 0, 7, 1, 0 }, fn.getpos('.'))
  1131. eq(false, api.nvim_get_option_value('paste', {}))
  1132. end)
  1133. it('Replace-mode', function()
  1134. -- Within single line
  1135. api.nvim_put({ 'aabbccdd', 'eeffgghh', 'iijjkkll' }, 'c', true, false)
  1136. command('normal l')
  1137. command('startreplace')
  1138. api.nvim_paste('123456', true, -1)
  1139. expect([[
  1140. a123456d
  1141. eeffgghh
  1142. iijjkkll]])
  1143. command('%delete _')
  1144. -- Across lines
  1145. api.nvim_put({ 'aabbccdd', 'eeffgghh', 'iijjkkll' }, 'c', true, false)
  1146. command('normal l')
  1147. command('startreplace')
  1148. api.nvim_paste('123\n456', true, -1)
  1149. expect([[
  1150. a123
  1151. 456d
  1152. eeffgghh
  1153. iijjkkll]])
  1154. end)
  1155. it('when searching in Visual mode', function()
  1156. feed('v/')
  1157. api.nvim_paste('aabbccdd', true, -1)
  1158. eq('aabbccdd', fn.getcmdline())
  1159. expect('')
  1160. end)
  1161. it('mappings are disabled in Cmdline mode', function()
  1162. command('cnoremap a b')
  1163. feed(':')
  1164. api.nvim_paste('a', true, -1)
  1165. eq('a', fn.getcmdline())
  1166. end)
  1167. it('pasted text is saved in cmdline history when <CR> comes from mapping #20957', function()
  1168. command('cnoremap <CR> <CR>')
  1169. feed(':')
  1170. api.nvim_paste('echo', true, -1)
  1171. eq('', fn.histget(':'))
  1172. feed('<CR>')
  1173. eq('echo', fn.histget(':'))
  1174. end)
  1175. it('pasting with empty last chunk in Cmdline mode', function()
  1176. local screen = Screen.new(20, 4)
  1177. feed(':')
  1178. api.nvim_paste('Foo', true, 1)
  1179. api.nvim_paste('', true, 3)
  1180. screen:expect([[
  1181. |
  1182. {1:~ }|*2
  1183. :Foo^ |
  1184. ]])
  1185. end)
  1186. it('pasting text with control characters in Cmdline mode', function()
  1187. local screen = Screen.new(20, 4)
  1188. feed(':')
  1189. api.nvim_paste('normal! \023\022\006\027', true, -1)
  1190. screen:expect([[
  1191. |
  1192. {1:~ }|*2
  1193. :normal! {18:^W^V^F^[}^ |
  1194. ]])
  1195. end)
  1196. it('crlf=false does not break lines at CR, CRLF', function()
  1197. api.nvim_paste('line 1\r\n\r\rline 2\nline 3\rline 4\r', false, -1)
  1198. local expected = 'line 1\r\n\r\rline 2\nline 3\rline 4\r'
  1199. expect(expected)
  1200. eq({ 0, 3, 14, 0 }, fn.getpos('.'))
  1201. feed('u') -- Undo.
  1202. expect('')
  1203. feed('.') -- Dot-repeat.
  1204. expect(expected)
  1205. end)
  1206. describe('repeating a paste via redo/recording', function()
  1207. -- Test with indent and control chars and multibyte chars containing 0x80 bytes
  1208. local text = dedent(([[
  1209. foo
  1210. bar
  1211. baz
  1212. !!!%s!!!%s!!!%s!!!
  1213. 最…倒…倀…
  1214. ]]):format('\0', '\2\3\6\21\22\23\24\27', '\127'))
  1215. before_each(function()
  1216. api.nvim_set_option_value('autoindent', true, {})
  1217. end)
  1218. local function test_paste_repeat_normal_insert(is_insert)
  1219. feed('qr' .. (is_insert and 'i' or ''))
  1220. eq('r', fn.reg_recording())
  1221. api.nvim_paste(text, true, -1)
  1222. feed(is_insert and '<Esc>' or '')
  1223. expect(text)
  1224. feed('.')
  1225. expect(text:rep(2))
  1226. feed('q')
  1227. eq('', fn.reg_recording())
  1228. feed('3.')
  1229. expect(text:rep(5))
  1230. feed('2@r')
  1231. expect(text:rep(9))
  1232. end
  1233. it('works in Normal mode', function()
  1234. test_paste_repeat_normal_insert(false)
  1235. end)
  1236. it('works in Insert mode', function()
  1237. test_paste_repeat_normal_insert(true)
  1238. end)
  1239. local function test_paste_repeat_visual_select(is_select)
  1240. insert(('xxx\n'):rep(5))
  1241. feed('ggqr' .. (is_select and 'gH' or 'V'))
  1242. api.nvim_paste(text, true, -1)
  1243. feed('q')
  1244. expect(text .. ('xxx\n'):rep(4))
  1245. feed('2@r')
  1246. expect(text:rep(3) .. ('xxx\n'):rep(2))
  1247. end
  1248. it('works in Visual mode (recording only)', function()
  1249. test_paste_repeat_visual_select(false)
  1250. end)
  1251. it('works in Select mode (recording only)', function()
  1252. test_paste_repeat_visual_select(true)
  1253. end)
  1254. end)
  1255. it('in a mapping recorded in a macro', function()
  1256. command([[nnoremap <F2> <Cmd>call nvim_paste('foo', v:false, -1)<CR>]])
  1257. feed('qr<F2>$q')
  1258. expect('foo')
  1259. feed('@r') -- repeating a macro containing the mapping should only paste once
  1260. expect('foofoo')
  1261. end)
  1262. local function test_paste_cancel_error(is_error)
  1263. before_each(function()
  1264. exec_lua(([[
  1265. vim.paste = (function(overridden)
  1266. return function(lines, phase)
  1267. for i, line in ipairs(lines) do
  1268. if line == 'CANCEL' then
  1269. %s
  1270. end
  1271. end
  1272. return overridden(lines, phase)
  1273. end
  1274. end)(vim.paste)
  1275. ]]):format(is_error and 'error("fake fail")' or 'return false'))
  1276. end)
  1277. local function check_paste_cancel_error(data, crlf, phase)
  1278. if is_error then
  1279. eq('fake fail', pcall_err(api.nvim_paste, data, crlf, phase))
  1280. else
  1281. eq(false, api.nvim_paste(data, crlf, phase))
  1282. end
  1283. end
  1284. it('in phase -1', function()
  1285. feed('A')
  1286. check_paste_cancel_error('CANCEL', true, -1)
  1287. feed('<Esc>')
  1288. expect('')
  1289. feed('.')
  1290. expect('')
  1291. end)
  1292. it('in phase 1', function()
  1293. feed('A')
  1294. check_paste_cancel_error('CANCEL', true, 1)
  1295. feed('<Esc>')
  1296. expect('')
  1297. feed('.')
  1298. expect('')
  1299. end)
  1300. it('in phase 2', function()
  1301. feed('A')
  1302. eq(true, api.nvim_paste('aaa', true, 1))
  1303. expect('aaa')
  1304. check_paste_cancel_error('CANCEL', true, 2)
  1305. feed('<Esc>')
  1306. expect('aaa')
  1307. feed('.')
  1308. expect('aaaaaa')
  1309. end)
  1310. it('in phase 3', function()
  1311. feed('A')
  1312. eq(true, api.nvim_paste('aaa', true, 1))
  1313. expect('aaa')
  1314. eq(true, api.nvim_paste('bbb', true, 2))
  1315. expect('aaabbb')
  1316. check_paste_cancel_error('CANCEL', true, 3)
  1317. feed('<Esc>')
  1318. expect('aaabbb')
  1319. feed('.')
  1320. expect('aaabbbaaabbb')
  1321. end)
  1322. end
  1323. describe('vim.paste() cancel', function()
  1324. test_paste_cancel_error(false)
  1325. end)
  1326. describe('vim.paste() error', function()
  1327. test_paste_cancel_error(true)
  1328. end)
  1329. end)
  1330. describe('nvim_put', function()
  1331. it('validation', function()
  1332. eq(
  1333. "Invalid 'line': expected String, got Integer",
  1334. pcall_err(request, 'nvim_put', { 42 }, 'l', false, false)
  1335. )
  1336. eq("Invalid 'type': 'x'", pcall_err(request, 'nvim_put', { 'foo' }, 'x', false, false))
  1337. end)
  1338. it("fails if 'nomodifiable'", function()
  1339. command('set nomodifiable')
  1340. eq(
  1341. [[Vim:E21: Cannot make changes, 'modifiable' is off]],
  1342. pcall_err(request, 'nvim_put', { 'a', 'b' }, 'l', true, true)
  1343. )
  1344. end)
  1345. it('inserts text', function()
  1346. -- linewise
  1347. api.nvim_put({ 'line 1', 'line 2', 'line 3' }, 'l', true, true)
  1348. expect([[
  1349. line 1
  1350. line 2
  1351. line 3]])
  1352. eq({ 0, 4, 1, 0 }, fn.getpos('.'))
  1353. command('%delete _')
  1354. -- charwise
  1355. api.nvim_put({ 'line 1', 'line 2', 'line 3' }, 'c', true, false)
  1356. expect([[
  1357. line 1
  1358. line 2
  1359. line 3]])
  1360. eq({ 0, 1, 1, 0 }, fn.getpos('.')) -- follow=false
  1361. -- blockwise
  1362. api.nvim_put({ 'AA', 'BB' }, 'b', true, true)
  1363. expect([[
  1364. lAAine 1
  1365. lBBine 2
  1366. line 3]])
  1367. eq({ 0, 2, 4, 0 }, fn.getpos('.'))
  1368. command('%delete _')
  1369. -- Empty lines list.
  1370. api.nvim_put({}, 'c', true, true)
  1371. eq({ 0, 1, 1, 0 }, fn.getpos('.'))
  1372. expect([[]])
  1373. -- Single empty line.
  1374. api.nvim_put({ '' }, 'c', true, true)
  1375. eq({ 0, 1, 1, 0 }, fn.getpos('.'))
  1376. expect([[
  1377. ]])
  1378. api.nvim_put({ 'AB' }, 'c', true, true)
  1379. -- after=false, follow=true
  1380. api.nvim_put({ 'line 1', 'line 2' }, 'c', false, true)
  1381. expect([[
  1382. Aline 1
  1383. line 2B]])
  1384. eq({ 0, 2, 7, 0 }, fn.getpos('.'))
  1385. command('%delete _')
  1386. api.nvim_put({ 'AB' }, 'c', true, true)
  1387. -- after=false, follow=false
  1388. api.nvim_put({ 'line 1', 'line 2' }, 'c', false, false)
  1389. expect([[
  1390. Aline 1
  1391. line 2B]])
  1392. eq({ 0, 1, 2, 0 }, fn.getpos('.'))
  1393. eq('', api.nvim_eval('v:errmsg'))
  1394. end)
  1395. it('detects charwise/linewise text (empty {type})', function()
  1396. -- linewise (final item is empty string)
  1397. api.nvim_put({ 'line 1', 'line 2', 'line 3', '' }, '', true, true)
  1398. expect([[
  1399. line 1
  1400. line 2
  1401. line 3]])
  1402. eq({ 0, 4, 1, 0 }, fn.getpos('.'))
  1403. command('%delete _')
  1404. -- charwise (final item is non-empty)
  1405. api.nvim_put({ 'line 1', 'line 2', 'line 3' }, '', true, true)
  1406. expect([[
  1407. line 1
  1408. line 2
  1409. line 3]])
  1410. eq({ 0, 3, 6, 0 }, fn.getpos('.'))
  1411. end)
  1412. it('allows block width', function()
  1413. -- behave consistently with setreg(); support "\022{NUM}" return by getregtype()
  1414. api.nvim_put({ 'line 1', 'line 2', 'line 3' }, 'l', false, false)
  1415. expect([[
  1416. line 1
  1417. line 2
  1418. line 3
  1419. ]])
  1420. -- larger width create spaces
  1421. api.nvim_put({ 'a', 'bc' }, 'b3', false, false)
  1422. expect([[
  1423. a line 1
  1424. bc line 2
  1425. line 3
  1426. ]])
  1427. -- smaller width is ignored
  1428. api.nvim_put({ 'xxx', 'yyy' }, '\0221', false, true)
  1429. expect([[
  1430. xxxa line 1
  1431. yyybc line 2
  1432. line 3
  1433. ]])
  1434. eq("Invalid 'type': 'bx'", pcall_err(api.nvim_put, { 'xxx', 'yyy' }, 'bx', false, true))
  1435. eq("Invalid 'type': 'b3x'", pcall_err(api.nvim_put, { 'xxx', 'yyy' }, 'b3x', false, true))
  1436. end)
  1437. end)
  1438. describe('nvim_strwidth', function()
  1439. it('works', function()
  1440. eq(3, api.nvim_strwidth('abc'))
  1441. -- 6 + (neovim)
  1442. -- 19 * 2 (each japanese character occupies two cells)
  1443. eq(44, api.nvim_strwidth('neovimのデザインかなりまともなのになってる。'))
  1444. end)
  1445. it('cannot handle NULs', function()
  1446. eq(0, api.nvim_strwidth('\0abc'))
  1447. end)
  1448. it('can handle emoji with variant selectors and ZWJ', function()
  1449. local selector = '❤️'
  1450. eq(2, fn.strchars(selector))
  1451. eq(1, fn.strcharlen(selector))
  1452. eq(2, api.nvim_strwidth(selector))
  1453. local no_selector = '❤'
  1454. eq(1, fn.strchars(no_selector))
  1455. eq(1, fn.strcharlen(no_selector))
  1456. eq(1, api.nvim_strwidth(no_selector))
  1457. local selector_zwj_selector = '🏳️‍⚧️'
  1458. eq(5, fn.strchars(selector_zwj_selector))
  1459. eq(1, fn.strcharlen(selector_zwj_selector))
  1460. eq(2, api.nvim_strwidth(selector_zwj_selector))
  1461. local emoji_zwj_emoji = '🧑‍🌾'
  1462. eq(3, fn.strchars(emoji_zwj_emoji))
  1463. eq(1, fn.strcharlen(emoji_zwj_emoji))
  1464. eq(2, api.nvim_strwidth(emoji_zwj_emoji))
  1465. end)
  1466. end)
  1467. describe('nvim_get_current_line, nvim_set_current_line', function()
  1468. it('works', function()
  1469. eq('', api.nvim_get_current_line())
  1470. api.nvim_set_current_line('abc')
  1471. eq('abc', api.nvim_get_current_line())
  1472. end)
  1473. end)
  1474. describe('set/get/del variables', function()
  1475. it('validation', function()
  1476. eq('Key not found: bogus', pcall_err(api.nvim_get_var, 'bogus'))
  1477. eq('Key not found: bogus', pcall_err(api.nvim_del_var, 'bogus'))
  1478. end)
  1479. it('nvim_get_var, nvim_set_var, nvim_del_var', function()
  1480. api.nvim_set_var('lua', { 1, 2, { ['3'] = 1 } })
  1481. eq({ 1, 2, { ['3'] = 1 } }, api.nvim_get_var('lua'))
  1482. eq({ 1, 2, { ['3'] = 1 } }, api.nvim_eval('g:lua'))
  1483. eq(1, fn.exists('g:lua'))
  1484. api.nvim_del_var('lua')
  1485. eq(0, fn.exists('g:lua'))
  1486. eq('Key not found: lua', pcall_err(api.nvim_del_var, 'lua'))
  1487. api.nvim_set_var('lua', 1)
  1488. -- Empty keys are allowed in Vim dicts (and msgpack).
  1489. api.nvim_set_var('dict_empty_key', { [''] = 'empty key' })
  1490. eq({ [''] = 'empty key' }, api.nvim_get_var('dict_empty_key'))
  1491. -- Set locked g: var.
  1492. command('lockvar lua')
  1493. eq('Key is locked: lua', pcall_err(api.nvim_del_var, 'lua'))
  1494. eq('Key is locked: lua', pcall_err(api.nvim_set_var, 'lua', 1))
  1495. exec([[
  1496. function Test()
  1497. endfunction
  1498. function s:Test()
  1499. endfunction
  1500. let g:Unknown_func = function('Test')
  1501. let g:Unknown_script_func = function('s:Test')
  1502. ]])
  1503. eq(NIL, api.nvim_get_var('Unknown_func'))
  1504. eq(NIL, api.nvim_get_var('Unknown_script_func'))
  1505. -- Check if autoload works properly
  1506. local pathsep = n.get_pathsep()
  1507. local xconfig = 'Xhome' .. pathsep .. 'Xconfig'
  1508. local xdata = 'Xhome' .. pathsep .. 'Xdata'
  1509. local autoload_folder = table.concat({ xconfig, 'nvim', 'autoload' }, pathsep)
  1510. local autoload_file = table.concat({ autoload_folder, 'testload.vim' }, pathsep)
  1511. mkdir_p(autoload_folder)
  1512. write_file(autoload_file, [[let testload#value = 2]])
  1513. clear { args_rm = { '-u' }, env = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata } }
  1514. eq(2, api.nvim_get_var('testload#value'))
  1515. rmdir('Xhome')
  1516. end)
  1517. it('nvim_get_vvar, nvim_set_vvar', function()
  1518. eq('Key is read-only: count', pcall_err(request, 'nvim_set_vvar', 'count', 42))
  1519. eq('Dict is locked', pcall_err(request, 'nvim_set_vvar', 'nosuchvar', 42))
  1520. api.nvim_set_vvar('errmsg', 'set by API')
  1521. eq('set by API', api.nvim_get_vvar('errmsg'))
  1522. api.nvim_set_vvar('completed_item', { word = 'a', user_data = vim.empty_dict() })
  1523. eq({}, api.nvim_get_vvar('completed_item')['user_data'])
  1524. api.nvim_set_vvar('errmsg', 42)
  1525. eq('42', eval('v:errmsg'))
  1526. api.nvim_set_vvar('oldfiles', { 'one', 'two' })
  1527. eq({ 'one', 'two' }, eval('v:oldfiles'))
  1528. api.nvim_set_vvar('oldfiles', {})
  1529. eq({}, eval('v:oldfiles'))
  1530. eq(
  1531. 'Setting v:oldfiles to value with wrong type',
  1532. pcall_err(api.nvim_set_vvar, 'oldfiles', 'a')
  1533. )
  1534. eq({}, eval('v:oldfiles'))
  1535. feed('i foo foo foo<Esc>0/foo<CR>')
  1536. eq({ 1, 1 }, api.nvim_win_get_cursor(0))
  1537. eq(1, eval('v:searchforward'))
  1538. feed('n')
  1539. eq({ 1, 5 }, api.nvim_win_get_cursor(0))
  1540. api.nvim_set_vvar('searchforward', 0)
  1541. eq(0, eval('v:searchforward'))
  1542. feed('n')
  1543. eq({ 1, 1 }, api.nvim_win_get_cursor(0))
  1544. api.nvim_set_vvar('searchforward', 1)
  1545. eq(1, eval('v:searchforward'))
  1546. feed('n')
  1547. eq({ 1, 5 }, api.nvim_win_get_cursor(0))
  1548. local screen = Screen.new(60, 3)
  1549. eq(1, eval('v:hlsearch'))
  1550. screen:expect {
  1551. grid = [[
  1552. {10:foo} {10:^foo} {10:foo} |
  1553. {1:~ }|
  1554. |
  1555. ]],
  1556. }
  1557. api.nvim_set_vvar('hlsearch', 0)
  1558. eq(0, eval('v:hlsearch'))
  1559. screen:expect {
  1560. grid = [[
  1561. foo ^foo foo |
  1562. {1:~ }|
  1563. |
  1564. ]],
  1565. }
  1566. api.nvim_set_vvar('hlsearch', 1)
  1567. eq(1, eval('v:hlsearch'))
  1568. screen:expect {
  1569. grid = [[
  1570. {10:foo} {10:^foo} {10:foo} |
  1571. {1:~ }|
  1572. |
  1573. ]],
  1574. }
  1575. end)
  1576. it('vim_set_var returns the old value', function()
  1577. local val1 = { 1, 2, { ['3'] = 1 } }
  1578. local val2 = { 4, 7 }
  1579. eq(NIL, request('vim_set_var', 'lua', val1))
  1580. eq(val1, request('vim_set_var', 'lua', val2))
  1581. end)
  1582. it('vim_del_var returns the old value', function()
  1583. local val1 = { 1, 2, { ['3'] = 1 } }
  1584. local val2 = { 4, 7 }
  1585. eq(NIL, request('vim_set_var', 'lua', val1))
  1586. eq(val1, request('vim_set_var', 'lua', val2))
  1587. eq(val2, request('vim_del_var', 'lua'))
  1588. end)
  1589. it('preserves values with NULs in them', function()
  1590. api.nvim_set_var('xxx', 'ab\0cd')
  1591. eq('ab\000cd', api.nvim_get_var('xxx'))
  1592. end)
  1593. end)
  1594. describe('nvim_get_option_value, nvim_set_option_value', function()
  1595. it('works', function()
  1596. ok(api.nvim_get_option_value('equalalways', {}))
  1597. api.nvim_set_option_value('equalalways', false, {})
  1598. ok(not api.nvim_get_option_value('equalalways', {}))
  1599. end)
  1600. it('works to get global value of local options', function()
  1601. eq(false, api.nvim_get_option_value('lisp', {}))
  1602. eq(8, api.nvim_get_option_value('shiftwidth', {}))
  1603. end)
  1604. it('works to set global value of local options', function()
  1605. api.nvim_set_option_value('lisp', true, { scope = 'global' })
  1606. eq(true, api.nvim_get_option_value('lisp', { scope = 'global' }))
  1607. eq(false, api.nvim_get_option_value('lisp', {}))
  1608. eq(nil, command_output('setglobal lisp?'):match('nolisp'))
  1609. eq('nolisp', command_output('setlocal lisp?'):match('nolisp'))
  1610. api.nvim_set_option_value('shiftwidth', 20, { scope = 'global' })
  1611. eq('20', command_output('setglobal shiftwidth?'):match('%d+'))
  1612. eq('8', command_output('setlocal shiftwidth?'):match('%d+'))
  1613. end)
  1614. it('updates where the option was last set from', function()
  1615. api.nvim_set_option_value('equalalways', false, {})
  1616. local status, rv = pcall(command_output, 'verbose set equalalways?')
  1617. eq(true, status)
  1618. matches('noequalalways\n' .. '\tLast set from API client %(channel id %d+%)', rv)
  1619. api.nvim_exec_lua('vim.api.nvim_set_option_value("equalalways", true, {})', {})
  1620. status, rv = pcall(command_output, 'verbose set equalalways?')
  1621. eq(true, status)
  1622. eq(' equalalways\n\tLast set from Lua (run Nvim with -V1 for more details)', rv)
  1623. end)
  1624. it('updates whether the option has ever been set #25025', function()
  1625. eq(false, api.nvim_get_option_info2('autochdir', {}).was_set)
  1626. api.nvim_set_option_value('autochdir', true, {})
  1627. eq(true, api.nvim_get_option_info2('autochdir', {}).was_set)
  1628. eq(false, api.nvim_get_option_info2('cmdwinheight', {}).was_set)
  1629. api.nvim_set_option_value('cmdwinheight', 10, {})
  1630. eq(true, api.nvim_get_option_info2('cmdwinheight', {}).was_set)
  1631. eq(false, api.nvim_get_option_info2('debug', {}).was_set)
  1632. api.nvim_set_option_value('debug', 'beep', {})
  1633. eq(true, api.nvim_get_option_info2('debug', {}).was_set)
  1634. end)
  1635. it('validation', function()
  1636. eq("Unknown option 'foobar'", pcall_err(api.nvim_set_option_value, 'foobar', 'baz', {}))
  1637. eq(
  1638. "Unknown option 'foobar'",
  1639. pcall_err(api.nvim_set_option_value, 'foobar', 'baz', { win = api.nvim_get_current_win() })
  1640. )
  1641. eq(
  1642. "Invalid 'scope': expected 'local' or 'global'",
  1643. pcall_err(api.nvim_get_option_value, 'scrolloff', { scope = 'bogus' })
  1644. )
  1645. eq(
  1646. "Invalid 'scope': expected 'local' or 'global'",
  1647. pcall_err(api.nvim_set_option_value, 'scrolloff', 1, { scope = 'bogus' })
  1648. )
  1649. eq(
  1650. "Invalid 'scope': expected String, got Integer",
  1651. pcall_err(api.nvim_get_option_value, 'scrolloff', { scope = 42 })
  1652. )
  1653. eq(
  1654. "Invalid 'value': expected valid option type, got Array",
  1655. pcall_err(api.nvim_set_option_value, 'scrolloff', {}, {})
  1656. )
  1657. eq(
  1658. "Invalid value for option 'scrolloff': expected number, got boolean true",
  1659. pcall_err(api.nvim_set_option_value, 'scrolloff', true, {})
  1660. )
  1661. eq(
  1662. 'Invalid value for option \'scrolloff\': expected number, got string "wrong"',
  1663. pcall_err(api.nvim_set_option_value, 'scrolloff', 'wrong', {})
  1664. )
  1665. end)
  1666. it('can get local values when global value is set', function()
  1667. eq(0, api.nvim_get_option_value('scrolloff', {}))
  1668. eq(-1, api.nvim_get_option_value('scrolloff', { scope = 'local' }))
  1669. end)
  1670. it('can set global and local values', function()
  1671. api.nvim_set_option_value('makeprg', 'hello', {})
  1672. eq('hello', api.nvim_get_option_value('makeprg', {}))
  1673. eq('', api.nvim_get_option_value('makeprg', { scope = 'local' }))
  1674. api.nvim_set_option_value('makeprg', 'world', { scope = 'local' })
  1675. eq('world', api.nvim_get_option_value('makeprg', { scope = 'local' }))
  1676. api.nvim_set_option_value('makeprg', 'goodbye', { scope = 'global' })
  1677. eq('goodbye', api.nvim_get_option_value('makeprg', { scope = 'global' }))
  1678. api.nvim_set_option_value('makeprg', 'hello', {})
  1679. eq('hello', api.nvim_get_option_value('makeprg', { scope = 'global' }))
  1680. eq('hello', api.nvim_get_option_value('makeprg', {}))
  1681. eq('', api.nvim_get_option_value('makeprg', { scope = 'local' }))
  1682. end)
  1683. it('clears the local value of an option with nil', function()
  1684. -- Set global value
  1685. api.nvim_set_option_value('shiftwidth', 42, {})
  1686. eq(42, api.nvim_get_option_value('shiftwidth', {}))
  1687. -- Set local value
  1688. api.nvim_set_option_value('shiftwidth', 8, { scope = 'local' })
  1689. eq(8, api.nvim_get_option_value('shiftwidth', {}))
  1690. eq(8, api.nvim_get_option_value('shiftwidth', { scope = 'local' }))
  1691. eq(42, api.nvim_get_option_value('shiftwidth', { scope = 'global' }))
  1692. -- Clear value without scope
  1693. api.nvim_set_option_value('shiftwidth', NIL, {})
  1694. eq(42, api.nvim_get_option_value('shiftwidth', {}))
  1695. eq(42, api.nvim_get_option_value('shiftwidth', { scope = 'local' }))
  1696. -- Clear value with explicit scope
  1697. api.nvim_set_option_value('shiftwidth', 8, { scope = 'local' })
  1698. api.nvim_set_option_value('shiftwidth', NIL, { scope = 'local' })
  1699. eq(42, api.nvim_get_option_value('shiftwidth', {}))
  1700. eq(42, api.nvim_get_option_value('shiftwidth', { scope = 'local' }))
  1701. -- Now try with options with a special "local is unset" value (e.g. 'undolevels')
  1702. api.nvim_set_option_value('undolevels', 1000, {})
  1703. api.nvim_set_option_value('undolevels', 1200, { scope = 'local' })
  1704. eq(1200, api.nvim_get_option_value('undolevels', { scope = 'local' }))
  1705. api.nvim_set_option_value('undolevels', NIL, { scope = 'local' })
  1706. eq(-123456, api.nvim_get_option_value('undolevels', { scope = 'local' }))
  1707. eq(1000, api.nvim_get_option_value('undolevels', {}))
  1708. api.nvim_set_option_value('autoread', true, {})
  1709. api.nvim_set_option_value('autoread', false, { scope = 'local' })
  1710. eq(false, api.nvim_get_option_value('autoread', { scope = 'local' }))
  1711. api.nvim_set_option_value('autoread', NIL, { scope = 'local' })
  1712. eq(NIL, api.nvim_get_option_value('autoread', { scope = 'local' }))
  1713. eq(true, api.nvim_get_option_value('autoread', {}))
  1714. end)
  1715. it('set window options', function()
  1716. api.nvim_set_option_value('colorcolumn', '4,3', {})
  1717. eq('4,3', api.nvim_get_option_value('colorcolumn', { scope = 'local' }))
  1718. command('set modified hidden')
  1719. command('enew') -- edit new buffer, window option is preserved
  1720. eq('4,3', api.nvim_get_option_value('colorcolumn', { scope = 'local' }))
  1721. end)
  1722. it('set local window options', function()
  1723. api.nvim_set_option_value('colorcolumn', '4,3', { win = 0, scope = 'local' })
  1724. eq('4,3', api.nvim_get_option_value('colorcolumn', { win = 0, scope = 'local' }))
  1725. command('set modified hidden')
  1726. command('enew') -- edit new buffer, window option is reset
  1727. eq('', api.nvim_get_option_value('colorcolumn', { win = 0, scope = 'local' }))
  1728. end)
  1729. it('get buffer or window-local options', function()
  1730. command('new')
  1731. local buf = api.nvim_get_current_buf()
  1732. api.nvim_set_option_value('tagfunc', 'foobar', { buf = buf })
  1733. eq('foobar', api.nvim_get_option_value('tagfunc', { buf = buf }))
  1734. local win = api.nvim_get_current_win()
  1735. api.nvim_set_option_value('number', true, { win = win })
  1736. eq(true, api.nvim_get_option_value('number', { win = win }))
  1737. end)
  1738. it('getting current buffer option does not adjust cursor #19381', function()
  1739. command('new')
  1740. local buf = api.nvim_get_current_buf()
  1741. print(vim.inspect(api.nvim_get_current_buf()))
  1742. local win = api.nvim_get_current_win()
  1743. insert('some text')
  1744. feed('0v$')
  1745. eq({ 1, 9 }, api.nvim_win_get_cursor(win))
  1746. api.nvim_get_option_value('filetype', { buf = buf })
  1747. eq({ 1, 9 }, api.nvim_win_get_cursor(win))
  1748. end)
  1749. it('can get default option values for filetypes', function()
  1750. command('filetype plugin on')
  1751. for ft, opts in pairs {
  1752. lua = { commentstring = '-- %s' },
  1753. vim = { commentstring = '"%s' },
  1754. man = { tagfunc = "v:lua.require'man'.goto_tag" },
  1755. xml = { formatexpr = 'xmlformat#Format()' },
  1756. } do
  1757. for option, value in pairs(opts) do
  1758. eq(value, api.nvim_get_option_value(option, { filetype = ft }))
  1759. end
  1760. end
  1761. command 'au FileType lua setlocal commentstring=NEW\\ %s'
  1762. eq('NEW %s', api.nvim_get_option_value('commentstring', { filetype = 'lua' }))
  1763. end)
  1764. it('errors for bad FileType autocmds', function()
  1765. command 'au FileType lua setlocal commentstring=BAD'
  1766. eq(
  1767. [[FileType Autocommands for "lua": Vim(setlocal):E537: 'commentstring' must be empty or contain %s: commentstring=BAD]],
  1768. pcall_err(api.nvim_get_option_value, 'commentstring', { filetype = 'lua' })
  1769. )
  1770. end)
  1771. it("value of 'modified' is always false for scratch buffers", function()
  1772. api.nvim_set_current_buf(api.nvim_create_buf(true, true))
  1773. insert([[
  1774. foo
  1775. bar
  1776. baz
  1777. ]])
  1778. eq(false, api.nvim_get_option_value('modified', {}))
  1779. end)
  1780. end)
  1781. describe('nvim_{get,set}_current_buf, nvim_list_bufs', function()
  1782. it('works', function()
  1783. eq(1, #api.nvim_list_bufs())
  1784. eq(api.nvim_list_bufs()[1], api.nvim_get_current_buf())
  1785. command('new')
  1786. eq(2, #api.nvim_list_bufs())
  1787. eq(api.nvim_list_bufs()[2], api.nvim_get_current_buf())
  1788. api.nvim_set_current_buf(api.nvim_list_bufs()[1])
  1789. eq(api.nvim_list_bufs()[1], api.nvim_get_current_buf())
  1790. end)
  1791. end)
  1792. describe('nvim_{get,set}_current_win, nvim_list_wins', function()
  1793. it('works', function()
  1794. eq(1, #api.nvim_list_wins())
  1795. eq(api.nvim_list_wins()[1], api.nvim_get_current_win())
  1796. command('vsplit')
  1797. command('split')
  1798. eq(3, #api.nvim_list_wins())
  1799. eq(api.nvim_list_wins()[1], api.nvim_get_current_win())
  1800. api.nvim_set_current_win(api.nvim_list_wins()[2])
  1801. eq(api.nvim_list_wins()[2], api.nvim_get_current_win())
  1802. end)
  1803. it('failure modes', function()
  1804. n.command('split')
  1805. eq('Invalid window id: 9999', pcall_err(api.nvim_set_current_win, 9999))
  1806. -- XXX: force nvim_set_current_win to fail somehow.
  1807. n.command("au WinLeave * throw 'foo'")
  1808. eq('WinLeave Autocommands for "*": foo', pcall_err(api.nvim_set_current_win, 1000))
  1809. end)
  1810. end)
  1811. describe('nvim_{get,set}_current_tabpage, nvim_list_tabpages', function()
  1812. it('works', function()
  1813. eq(1, #api.nvim_list_tabpages())
  1814. eq(api.nvim_list_tabpages()[1], api.nvim_get_current_tabpage())
  1815. command('tabnew')
  1816. eq(2, #api.nvim_list_tabpages())
  1817. eq(2, #api.nvim_list_wins())
  1818. eq(api.nvim_list_wins()[2], api.nvim_get_current_win())
  1819. eq(api.nvim_list_tabpages()[2], api.nvim_get_current_tabpage())
  1820. api.nvim_set_current_win(api.nvim_list_wins()[1])
  1821. -- Switching window also switches tabpages if necessary
  1822. eq(api.nvim_list_tabpages()[1], api.nvim_get_current_tabpage())
  1823. eq(api.nvim_list_wins()[1], api.nvim_get_current_win())
  1824. api.nvim_set_current_tabpage(api.nvim_list_tabpages()[2])
  1825. eq(api.nvim_list_tabpages()[2], api.nvim_get_current_tabpage())
  1826. eq(api.nvim_list_wins()[2], api.nvim_get_current_win())
  1827. end)
  1828. it('failure modes', function()
  1829. n.command('tabnew')
  1830. eq('Invalid tabpage id: 999', pcall_err(api.nvim_set_current_tabpage, 999))
  1831. -- XXX: force nvim_set_current_tabpage to fail somehow.
  1832. n.command("au TabLeave * throw 'foo'")
  1833. eq('TabLeave Autocommands for "*": foo', pcall_err(api.nvim_set_current_tabpage, 1))
  1834. end)
  1835. end)
  1836. describe('nvim_get_mode', function()
  1837. it('during normal-mode `g` returns blocking=true', function()
  1838. api.nvim_input('o') -- add a line
  1839. eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
  1840. api.nvim_input([[<C-\><C-N>]])
  1841. eq(2, api.nvim_eval("line('.')"))
  1842. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1843. api.nvim_input('g')
  1844. eq({ mode = 'n', blocking = true }, api.nvim_get_mode())
  1845. api.nvim_input('k') -- complete the operator
  1846. eq(1, api.nvim_eval("line('.')")) -- verify the completed operator
  1847. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1848. end)
  1849. it('returns the correct result multiple consecutive times', function()
  1850. for _ = 1, 5 do
  1851. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1852. end
  1853. api.nvim_input('g')
  1854. for _ = 1, 4 do
  1855. eq({ mode = 'n', blocking = true }, api.nvim_get_mode())
  1856. end
  1857. api.nvim_input('g')
  1858. for _ = 1, 7 do
  1859. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1860. end
  1861. end)
  1862. it('during normal-mode CTRL-W, returns blocking=true', function()
  1863. api.nvim_input('<C-W>')
  1864. eq({ mode = 'n', blocking = true }, api.nvim_get_mode())
  1865. api.nvim_input('s') -- complete the operator
  1866. eq(2, api.nvim_eval("winnr('$')")) -- verify the completed operator
  1867. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1868. end)
  1869. it('during press-enter prompt without UI returns blocking=false', function()
  1870. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1871. command("echom 'msg1'")
  1872. command("echom 'msg2'")
  1873. command("echom 'msg3'")
  1874. command("echom 'msg4'")
  1875. command("echom 'msg5'")
  1876. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1877. api.nvim_input(':messages<CR>')
  1878. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1879. end)
  1880. it('during press-enter prompt returns blocking=true', function()
  1881. api.nvim_ui_attach(80, 20, {})
  1882. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1883. command("echom 'msg1'")
  1884. command("echom 'msg2'")
  1885. command("echom 'msg3'")
  1886. command("echom 'msg4'")
  1887. command("echom 'msg5'")
  1888. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1889. api.nvim_input(':messages<CR>')
  1890. eq({ mode = 'r', blocking = true }, api.nvim_get_mode())
  1891. end)
  1892. it('during getchar() returns blocking=false', function()
  1893. api.nvim_input(':let g:test_input = nr2char(getchar())<CR>')
  1894. -- Events are enabled during getchar(), RPC calls are *not* blocked. #5384
  1895. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1896. eq(0, api.nvim_eval("exists('g:test_input')"))
  1897. api.nvim_input('J')
  1898. eq('J', api.nvim_eval('g:test_input'))
  1899. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1900. end)
  1901. -- TODO: bug #6247#issuecomment-286403810
  1902. it('batched with input', function()
  1903. api.nvim_ui_attach(80, 20, {})
  1904. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1905. command("echom 'msg1'")
  1906. command("echom 'msg2'")
  1907. command("echom 'msg3'")
  1908. command("echom 'msg4'")
  1909. command("echom 'msg5'")
  1910. local req = {
  1911. { 'nvim_get_mode', {} },
  1912. { 'nvim_input', { ':messages<CR>' } },
  1913. { 'nvim_get_mode', {} },
  1914. { 'nvim_eval', { '1' } },
  1915. }
  1916. eq({
  1917. {
  1918. { mode = 'n', blocking = false },
  1919. 13,
  1920. { mode = 'n', blocking = false }, -- TODO: should be blocked=true ?
  1921. 1,
  1922. },
  1923. NIL,
  1924. }, api.nvim_call_atomic(req))
  1925. eq({ mode = 'r', blocking = true }, api.nvim_get_mode())
  1926. end)
  1927. it('during insert-mode map-pending, returns blocking=true #6166', function()
  1928. command('inoremap xx foo')
  1929. api.nvim_input('ix')
  1930. eq({ mode = 'i', blocking = true }, api.nvim_get_mode())
  1931. end)
  1932. it('during normal-mode gU, returns blocking=false #6166', function()
  1933. api.nvim_input('gu')
  1934. eq({ mode = 'no', blocking = false }, api.nvim_get_mode())
  1935. end)
  1936. it("at '-- More --' prompt returns blocking=true #11899", function()
  1937. command('set more')
  1938. feed(':digraphs<cr>')
  1939. eq({ mode = 'rm', blocking = true }, api.nvim_get_mode())
  1940. end)
  1941. it('after <Nop> mapping returns blocking=false #17257', function()
  1942. command('nnoremap <F2> <Nop>')
  1943. feed('<F2>')
  1944. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1945. end)
  1946. it('after empty string <expr> mapping returns blocking=false #17257', function()
  1947. command('nnoremap <expr> <F2> ""')
  1948. feed('<F2>')
  1949. eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
  1950. end)
  1951. end)
  1952. describe('RPC (K_EVENT)', function()
  1953. it('does not complete ("interrupt") normal-mode operator-pending #6166', function()
  1954. n.insert([[
  1955. FIRST LINE
  1956. SECOND LINE]])
  1957. api.nvim_input('gg')
  1958. api.nvim_input('gu')
  1959. -- Make any RPC request (can be non-async: op-pending does not block).
  1960. api.nvim_get_current_buf()
  1961. -- Buffer should not change.
  1962. expect([[
  1963. FIRST LINE
  1964. SECOND LINE]])
  1965. -- Now send input to complete the operator.
  1966. api.nvim_input('j')
  1967. expect([[
  1968. first line
  1969. second line]])
  1970. end)
  1971. it('does not complete ("interrupt") `d` #3732', function()
  1972. local screen = Screen.new(20, 4)
  1973. command('set listchars=eol:$')
  1974. command('set list')
  1975. feed('ia<cr>b<cr>c<cr><Esc>kkk')
  1976. feed('d')
  1977. -- Make any RPC request (can be non-async: op-pending does not block).
  1978. api.nvim_get_current_buf()
  1979. screen:expect([[
  1980. ^a{1:$} |
  1981. b{1:$} |
  1982. c{1:$} |
  1983. |
  1984. ]])
  1985. end)
  1986. it('does not complete ("interrupt") normal-mode map-pending #6166', function()
  1987. command("nnoremap dd :let g:foo='it worked...'<CR>")
  1988. n.insert([[
  1989. FIRST LINE
  1990. SECOND LINE]])
  1991. api.nvim_input('gg')
  1992. api.nvim_input('d')
  1993. -- Make any RPC request (must be async, because map-pending blocks).
  1994. api.nvim_get_api_info()
  1995. -- Send input to complete the mapping.
  1996. api.nvim_input('d')
  1997. expect([[
  1998. FIRST LINE
  1999. SECOND LINE]])
  2000. eq('it worked...', n.eval('g:foo'))
  2001. end)
  2002. it('does not complete ("interrupt") insert-mode map-pending #6166', function()
  2003. command('inoremap xx foo')
  2004. command('set timeoutlen=9999')
  2005. n.insert([[
  2006. FIRST LINE
  2007. SECOND LINE]])
  2008. api.nvim_input('ix')
  2009. -- Make any RPC request (must be async, because map-pending blocks).
  2010. api.nvim_get_api_info()
  2011. -- Send input to complete the mapping.
  2012. api.nvim_input('x')
  2013. expect([[
  2014. FIRST LINE
  2015. SECOND LINfooE]])
  2016. end)
  2017. it('does not interrupt Insert mode i_CTRL-O #10035', function()
  2018. feed('iHello World<c-o>')
  2019. eq({ mode = 'niI', blocking = false }, api.nvim_get_mode()) -- fast event
  2020. eq(2, eval('1+1')) -- causes K_EVENT key
  2021. eq({ mode = 'niI', blocking = false }, api.nvim_get_mode()) -- still in ctrl-o mode
  2022. feed('dd')
  2023. eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) -- left ctrl-o mode
  2024. expect('') -- executed the command
  2025. end)
  2026. it('does not interrupt Select mode v_CTRL-O #15688', function()
  2027. feed('iHello World<esc>gh<c-o>')
  2028. eq({ mode = 'vs', blocking = false }, api.nvim_get_mode()) -- fast event
  2029. eq({ mode = 'vs', blocking = false }, api.nvim_get_mode()) -- again #15288
  2030. eq(2, eval('1+1')) -- causes K_EVENT key
  2031. eq({ mode = 'vs', blocking = false }, api.nvim_get_mode()) -- still in ctrl-o mode
  2032. feed('^')
  2033. eq({ mode = 's', blocking = false }, api.nvim_get_mode()) -- left ctrl-o mode
  2034. feed('h')
  2035. eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) -- entered insert mode
  2036. expect('h') -- selection is the whole line and is replaced
  2037. end)
  2038. it('does not interrupt Insert mode i_0_CTRL-D #13997', function()
  2039. command('set timeoutlen=9999')
  2040. feed('i<Tab><Tab>a0')
  2041. eq(2, eval('1+1')) -- causes K_EVENT key
  2042. feed('<C-D>')
  2043. expect('a') -- recognized i_0_CTRL-D
  2044. end)
  2045. it("does not interrupt with 'digraph'", function()
  2046. command('set digraph')
  2047. feed('i,')
  2048. eq(2, eval('1+1')) -- causes K_EVENT key
  2049. feed('<BS>')
  2050. eq(2, eval('1+1')) -- causes K_EVENT key
  2051. feed('.')
  2052. expect('…') -- digraph ",." worked
  2053. feed('<Esc>')
  2054. feed(':,')
  2055. eq(2, eval('1+1')) -- causes K_EVENT key
  2056. feed('<BS>')
  2057. eq(2, eval('1+1')) -- causes K_EVENT key
  2058. feed('.')
  2059. eq('…', fn.getcmdline()) -- digraph ",." worked
  2060. end)
  2061. end)
  2062. describe('nvim_get_context', function()
  2063. it('validation', function()
  2064. eq("Invalid key: 'blah'", pcall_err(api.nvim_get_context, { blah = {} }))
  2065. eq(
  2066. "Invalid 'types': expected Array, got Integer",
  2067. pcall_err(api.nvim_get_context, { types = 42 })
  2068. )
  2069. eq(
  2070. "Invalid 'type': 'zub'",
  2071. pcall_err(api.nvim_get_context, { types = { 'jumps', 'zub', 'zam' } })
  2072. )
  2073. end)
  2074. it('returns map of current editor state', function()
  2075. local opts = { types = { 'regs', 'jumps', 'bufs', 'gvars' } }
  2076. eq({}, parse_context(api.nvim_get_context({})))
  2077. feed('i1<cr>2<cr>3<c-[>ddddddqahjklquuu')
  2078. feed('gg')
  2079. feed('G')
  2080. command('edit! BUF1')
  2081. command('edit BUF2')
  2082. api.nvim_set_var('one', 1)
  2083. api.nvim_set_var('Two', 2)
  2084. api.nvim_set_var('THREE', 3)
  2085. local expected_ctx = {
  2086. ['regs'] = {
  2087. { ['rt'] = 1, ['rc'] = { '1' }, ['n'] = 49, ['ru'] = true },
  2088. { ['rt'] = 1, ['rc'] = { '2' }, ['n'] = 50 },
  2089. { ['rt'] = 1, ['rc'] = { '3' }, ['n'] = 51 },
  2090. { ['rc'] = { 'hjkl' }, ['n'] = 97 },
  2091. },
  2092. ['jumps'] = eval((([[
  2093. filter(map(add(
  2094. getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }),
  2095. 'filter(
  2096. { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
  2097. { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
  2098. ]]):gsub('\n', ''))),
  2099. ['bufs'] = eval([[
  2100. filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)')
  2101. ]]),
  2102. ['gvars'] = { { 'one', 1 }, { 'Two', 2 }, { 'THREE', 3 } },
  2103. }
  2104. eq(expected_ctx, parse_context(api.nvim_get_context(opts)))
  2105. eq(expected_ctx, parse_context(api.nvim_get_context({})))
  2106. eq(expected_ctx, parse_context(api.nvim_get_context({ types = {} })))
  2107. end)
  2108. end)
  2109. describe('nvim_load_context', function()
  2110. it('sets current editor state to given context dict', function()
  2111. local opts = { types = { 'regs', 'jumps', 'bufs', 'gvars' } }
  2112. eq({}, parse_context(api.nvim_get_context(opts)))
  2113. api.nvim_set_var('one', 1)
  2114. api.nvim_set_var('Two', 2)
  2115. api.nvim_set_var('THREE', 3)
  2116. local ctx = api.nvim_get_context(opts)
  2117. api.nvim_set_var('one', 'a')
  2118. api.nvim_set_var('Two', 'b')
  2119. api.nvim_set_var('THREE', 'c')
  2120. eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
  2121. api.nvim_load_context(ctx)
  2122. eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
  2123. end)
  2124. it('errors when context dict is invalid', function()
  2125. eq(
  2126. 'E474: Failed to convert list to msgpack string buffer',
  2127. pcall_err(api.nvim_load_context, { regs = { {} }, jumps = { {} } })
  2128. )
  2129. eq(
  2130. 'E474: Failed to convert list to msgpack string buffer',
  2131. pcall_err(api.nvim_load_context, { regs = { { [''] = '' } } })
  2132. )
  2133. end)
  2134. end)
  2135. describe('nvim_replace_termcodes', function()
  2136. it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function()
  2137. eq('\128\254X', n.api.nvim_replace_termcodes('\128', true, true, true))
  2138. end)
  2139. it('leaves non-K_SPECIAL string unchanged', function()
  2140. eq('abc', n.api.nvim_replace_termcodes('abc', true, true, true))
  2141. end)
  2142. it('converts <expressions>', function()
  2143. eq('\\', n.api.nvim_replace_termcodes('<Leader>', true, true, true))
  2144. end)
  2145. it('converts <LeftMouse> to K_SPECIAL KS_EXTRA KE_LEFTMOUSE', function()
  2146. -- K_SPECIAL KS_EXTRA KE_LEFTMOUSE
  2147. -- 0x80 0xfd 0x2c
  2148. -- 128 253 44
  2149. eq('\128\253\44', n.api.nvim_replace_termcodes('<LeftMouse>', true, true, true))
  2150. end)
  2151. it('converts keycodes', function()
  2152. eq('\nx\27x\rx<x', n.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, true))
  2153. end)
  2154. it('does not convert keycodes if special=false', function()
  2155. eq(
  2156. '<NL>x<Esc>x<CR>x<lt>x',
  2157. n.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, false)
  2158. )
  2159. end)
  2160. it('does not crash when transforming an empty string', function()
  2161. -- Actually does not test anything, because current code will use NULL for
  2162. -- an empty string.
  2163. --
  2164. -- Problem here is that if String argument has .data in allocated memory
  2165. -- then `return str` in vim_replace_termcodes body will make Neovim free
  2166. -- `str.data` twice: once when freeing arguments, then when freeing return
  2167. -- value.
  2168. eq('', api.nvim_replace_termcodes('', true, true, true))
  2169. end)
  2170. -- Not exactly the case, as nvim_replace_termcodes() escapes K_SPECIAL in Unicode
  2171. it('translates the result of keytrans() on string with 0x80 byte back', function()
  2172. local s = 'ff\128\253\097tt'
  2173. eq(s, api.nvim_replace_termcodes(fn.keytrans(s), true, true, true))
  2174. end)
  2175. end)
  2176. describe('nvim_feedkeys', function()
  2177. it('K_SPECIAL escaping', function()
  2178. local function on_setup()
  2179. -- notice the special char(…) \xe2\80\xa6
  2180. api.nvim_feedkeys(':let x1="…"\n', '', true)
  2181. -- Both nvim_replace_termcodes and nvim_feedkeys escape \x80
  2182. local inp = n.api.nvim_replace_termcodes(':let x2="…"<CR>', true, true, true)
  2183. api.nvim_feedkeys(inp, '', true) -- escape_ks=true
  2184. -- nvim_feedkeys with K_SPECIAL escaping disabled
  2185. inp = n.api.nvim_replace_termcodes(':let x3="…"<CR>', true, true, true)
  2186. api.nvim_feedkeys(inp, '', false) -- escape_ks=false
  2187. n.stop()
  2188. end
  2189. -- spin the loop a bit
  2190. n.run(nil, nil, on_setup)
  2191. eq('…', api.nvim_get_var('x1'))
  2192. -- Because of the double escaping this is neq
  2193. neq('…', api.nvim_get_var('x2'))
  2194. eq('…', api.nvim_get_var('x3'))
  2195. end)
  2196. end)
  2197. describe('nvim_out_write', function()
  2198. local screen
  2199. before_each(function()
  2200. screen = Screen.new(40, 8)
  2201. end)
  2202. it('prints long messages correctly #20534', function()
  2203. exec([[
  2204. set more
  2205. redir => g:out
  2206. silent! call nvim_out_write('a')
  2207. silent! call nvim_out_write('a')
  2208. silent! call nvim_out_write('a')
  2209. silent! call nvim_out_write("\n")
  2210. silent! call nvim_out_write('a')
  2211. silent! call nvim_out_write('a')
  2212. silent! call nvim_out_write(repeat('a', 5000) .. "\n")
  2213. silent! call nvim_out_write('a')
  2214. silent! call nvim_out_write('a')
  2215. silent! call nvim_out_write('a')
  2216. silent! call nvim_out_write("\n")
  2217. redir END
  2218. ]])
  2219. eq('\naaa\n' .. ('a'):rep(5002) .. '\naaa', api.nvim_get_var('out'))
  2220. end)
  2221. it('blank line in message', function()
  2222. feed([[:call nvim_out_write("\na\n")<CR>]])
  2223. screen:expect {
  2224. grid = [[
  2225. |
  2226. {1:~ }|*3
  2227. {3: }|
  2228. |
  2229. a |
  2230. {6:Press ENTER or type command to continue}^ |
  2231. ]],
  2232. }
  2233. feed('<CR>')
  2234. feed([[:call nvim_out_write("b\n\nc\n")<CR>]])
  2235. screen:expect {
  2236. grid = [[
  2237. |
  2238. {1:~ }|*2
  2239. {3: }|
  2240. b |
  2241. |
  2242. c |
  2243. {6:Press ENTER or type command to continue}^ |
  2244. ]],
  2245. }
  2246. end)
  2247. it('NUL bytes in message', function()
  2248. feed([[:lua vim.api.nvim_out_write('aaa\0bbb\0\0ccc\nddd\0\0\0eee\n')<CR>]])
  2249. screen:expect {
  2250. grid = [[
  2251. |
  2252. {1:~ }|*3
  2253. {3: }|
  2254. aaa{18:^@}bbb{18:^@^@}ccc |
  2255. ddd{18:^@^@^@}eee |
  2256. {6:Press ENTER or type command to continue}^ |
  2257. ]],
  2258. }
  2259. end)
  2260. end)
  2261. describe('nvim_err_write', function()
  2262. local screen
  2263. before_each(function()
  2264. screen = Screen.new(40, 8)
  2265. end)
  2266. it('can show one line', function()
  2267. async_meths.nvim_err_write('has bork\n')
  2268. screen:expect([[
  2269. ^ |
  2270. {1:~ }|*6
  2271. {9:has bork} |
  2272. ]])
  2273. end)
  2274. it('shows return prompt when more than &cmdheight lines', function()
  2275. async_meths.nvim_err_write('something happened\nvery bad\n')
  2276. screen:expect([[
  2277. |
  2278. {1:~ }|*3
  2279. {3: }|
  2280. {9:something happened} |
  2281. {9:very bad} |
  2282. {6:Press ENTER or type command to continue}^ |
  2283. ]])
  2284. end)
  2285. it('shows return prompt after all lines are shown', function()
  2286. async_meths.nvim_err_write('FAILURE\nERROR\nEXCEPTION\nTRACEBACK\n')
  2287. screen:expect([[
  2288. |
  2289. {1:~ }|
  2290. {3: }|
  2291. {9:FAILURE} |
  2292. {9:ERROR} |
  2293. {9:EXCEPTION} |
  2294. {9:TRACEBACK} |
  2295. {6:Press ENTER or type command to continue}^ |
  2296. ]])
  2297. end)
  2298. it('handles multiple calls', function()
  2299. -- without linebreak text is joined to one line
  2300. async_meths.nvim_err_write('very ')
  2301. async_meths.nvim_err_write('fail\n')
  2302. screen:expect([[
  2303. ^ |
  2304. {1:~ }|*6
  2305. {9:very fail} |
  2306. ]])
  2307. n.poke_eventloop()
  2308. -- shows up to &cmdheight lines
  2309. async_meths.nvim_err_write('more fail\ntoo fail\n')
  2310. screen:expect([[
  2311. |
  2312. {1:~ }|*3
  2313. {3: }|
  2314. {9:more fail} |
  2315. {9:too fail} |
  2316. {6:Press ENTER or type command to continue}^ |
  2317. ]])
  2318. feed('<cr>') -- exit the press ENTER screen
  2319. end)
  2320. it('NUL bytes in message', function()
  2321. async_meths.nvim_err_write('aaa\0bbb\0\0ccc\nddd\0\0\0eee\n')
  2322. screen:expect {
  2323. grid = [[
  2324. |
  2325. {1:~ }|*3
  2326. {3: }|
  2327. {9:aaa^@bbb^@^@ccc} |
  2328. {9:ddd^@^@^@eee} |
  2329. {6:Press ENTER or type command to continue}^ |
  2330. ]],
  2331. }
  2332. end)
  2333. end)
  2334. describe('nvim_err_writeln', function()
  2335. local screen
  2336. before_each(function()
  2337. screen = Screen.new(40, 8)
  2338. end)
  2339. it('shows only one return prompt after all lines are shown', function()
  2340. async_meths.nvim_err_writeln('FAILURE\nERROR\nEXCEPTION\nTRACEBACK')
  2341. screen:expect([[
  2342. |
  2343. {1:~ }|
  2344. {3: }|
  2345. {9:FAILURE} |
  2346. {9:ERROR} |
  2347. {9:EXCEPTION} |
  2348. {9:TRACEBACK} |
  2349. {6:Press ENTER or type command to continue}^ |
  2350. ]])
  2351. feed('<CR>')
  2352. screen:expect([[
  2353. ^ |
  2354. {1:~ }|*6
  2355. |
  2356. ]])
  2357. end)
  2358. end)
  2359. describe('nvim_list_chans, nvim_get_chan_info', function()
  2360. before_each(function()
  2361. command('autocmd ChanOpen * let g:opened_event = deepcopy(v:event)')
  2362. command('autocmd ChanInfo * let g:info_event = deepcopy(v:event)')
  2363. end)
  2364. local testinfo = {
  2365. stream = 'stdio',
  2366. id = 1,
  2367. mode = 'rpc',
  2368. client = {},
  2369. }
  2370. local stderr = {
  2371. stream = 'stderr',
  2372. id = 2,
  2373. mode = 'bytes',
  2374. }
  2375. it('returns {} for invalid channel', function()
  2376. eq({}, api.nvim_get_chan_info(-1))
  2377. -- more preallocated numbers might be added, try something high
  2378. eq({}, api.nvim_get_chan_info(10))
  2379. end)
  2380. it('stream=stdio channel', function()
  2381. eq({ [1] = testinfo, [2] = stderr }, api.nvim_list_chans())
  2382. -- 0 should return current channel
  2383. eq(testinfo, api.nvim_get_chan_info(0))
  2384. eq(testinfo, api.nvim_get_chan_info(1))
  2385. eq(stderr, api.nvim_get_chan_info(2))
  2386. api.nvim_set_client_info(
  2387. 'functionaltests',
  2388. { major = 0, minor = 3, patch = 17 },
  2389. 'ui',
  2390. { do_stuff = { n_args = { 2, 3 } } },
  2391. { license = 'Apache2' }
  2392. )
  2393. local info = {
  2394. stream = 'stdio',
  2395. id = 1,
  2396. mode = 'rpc',
  2397. client = {
  2398. name = 'functionaltests',
  2399. version = { major = 0, minor = 3, patch = 17 },
  2400. type = 'ui',
  2401. methods = { do_stuff = { n_args = { 2, 3 } } },
  2402. attributes = { license = 'Apache2' },
  2403. },
  2404. }
  2405. eq({ info = info }, api.nvim_get_var('info_event'))
  2406. eq({ [1] = info, [2] = stderr }, api.nvim_list_chans())
  2407. eq(info, api.nvim_get_chan_info(1))
  2408. end)
  2409. it('stream=job channel', function()
  2410. eq(3, eval("jobstart(['cat'], {'rpc': v:true})"))
  2411. local catpath = eval('exepath("cat")')
  2412. local info = {
  2413. stream = 'job',
  2414. id = 3,
  2415. argv = { catpath },
  2416. mode = 'rpc',
  2417. client = {},
  2418. }
  2419. eq({ info = info }, api.nvim_get_var('opened_event'))
  2420. eq({ [1] = testinfo, [2] = stderr, [3] = info }, api.nvim_list_chans())
  2421. eq(info, api.nvim_get_chan_info(3))
  2422. eval(
  2423. 'rpcrequest(3, "nvim_set_client_info", "amazing-cat", {}, "remote",'
  2424. .. '{"nvim_command":{"n_args":1}},' -- and so on
  2425. .. '{"description":"The Amazing Cat"})'
  2426. )
  2427. info = {
  2428. stream = 'job',
  2429. id = 3,
  2430. argv = { catpath },
  2431. mode = 'rpc',
  2432. client = {
  2433. name = 'amazing-cat',
  2434. version = { major = 0 },
  2435. type = 'remote',
  2436. methods = { nvim_command = { n_args = 1 } },
  2437. attributes = { description = 'The Amazing Cat' },
  2438. },
  2439. }
  2440. eq({ info = info }, api.nvim_get_var('info_event'))
  2441. eq({ [1] = testinfo, [2] = stderr, [3] = info }, api.nvim_list_chans())
  2442. eq(
  2443. "Vim:Error invoking 'nvim_set_current_buf' on channel 3 (amazing-cat):\nWrong type for argument 1 when calling nvim_set_current_buf, expecting Buffer",
  2444. pcall_err(eval, 'rpcrequest(3, "nvim_set_current_buf", -1)')
  2445. )
  2446. eq(info, eval('rpcrequest(3, "nvim_get_chan_info", 0)'))
  2447. end)
  2448. it('stream=job :terminal channel', function()
  2449. command(':terminal')
  2450. eq(1, api.nvim_get_current_buf())
  2451. eq(3, api.nvim_get_option_value('channel', { buf = 1 }))
  2452. local info = {
  2453. stream = 'job',
  2454. id = 3,
  2455. argv = { eval('exepath(&shell)') },
  2456. mode = 'terminal',
  2457. buffer = 1,
  2458. pty = '?',
  2459. }
  2460. local event = api.nvim_get_var('opened_event')
  2461. if not is_os('win') then
  2462. info.pty = event.info.pty
  2463. neq(nil, string.match(info.pty, '^/dev/'))
  2464. end
  2465. eq({ info = info }, event)
  2466. info.buffer = 1
  2467. eq({ [1] = testinfo, [2] = stderr, [3] = info }, api.nvim_list_chans())
  2468. eq(info, api.nvim_get_chan_info(3))
  2469. -- :terminal with args + running process.
  2470. command('enew')
  2471. local progpath_esc = eval('shellescape(v:progpath)')
  2472. fn.jobstart(('%s -u NONE -i NONE'):format(progpath_esc), {
  2473. term = true,
  2474. env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
  2475. })
  2476. eq(-1, eval('jobwait([&channel], 0)[0]')) -- Running?
  2477. local expected2 = {
  2478. stream = 'job',
  2479. id = 4,
  2480. argv = (is_os('win') and {
  2481. eval('&shell'),
  2482. '/s',
  2483. '/c',
  2484. fmt('"%s -u NONE -i NONE"', progpath_esc),
  2485. } or {
  2486. eval('&shell'),
  2487. eval('&shellcmdflag'),
  2488. fmt('%s -u NONE -i NONE', progpath_esc),
  2489. }),
  2490. mode = 'terminal',
  2491. buffer = 2,
  2492. pty = '?',
  2493. }
  2494. local actual2 = eval('nvim_get_chan_info(&channel)')
  2495. expected2.pty = actual2.pty
  2496. eq(expected2, actual2)
  2497. -- :terminal with args + stopped process.
  2498. eq(1, eval('jobstop(&channel)'))
  2499. eval('jobwait([&channel], 1000)') -- Wait.
  2500. expected2.pty = (is_os('win') and '?' or '') -- pty stream was closed.
  2501. eq(expected2, eval('nvim_get_chan_info(&channel)'))
  2502. end)
  2503. end)
  2504. describe('nvim_call_atomic', function()
  2505. it('works', function()
  2506. api.nvim_buf_set_lines(0, 0, -1, true, { 'first' })
  2507. local req = {
  2508. { 'nvim_get_current_line', {} },
  2509. { 'nvim_set_current_line', { 'second' } },
  2510. }
  2511. eq({ { 'first', NIL }, NIL }, api.nvim_call_atomic(req))
  2512. eq({ 'second' }, api.nvim_buf_get_lines(0, 0, -1, true))
  2513. end)
  2514. it('allows multiple return values', function()
  2515. local req = {
  2516. { 'nvim_set_var', { 'avar', true } },
  2517. { 'nvim_set_var', { 'bvar', 'string' } },
  2518. { 'nvim_get_var', { 'avar' } },
  2519. { 'nvim_get_var', { 'bvar' } },
  2520. }
  2521. eq({ { NIL, NIL, true, 'string' }, NIL }, api.nvim_call_atomic(req))
  2522. end)
  2523. it('is aborted by errors in call', function()
  2524. local error_types = api.nvim_get_api_info()[2].error_types
  2525. local req = {
  2526. { 'nvim_set_var', { 'one', 1 } },
  2527. { 'nvim_buf_set_lines', {} },
  2528. { 'nvim_set_var', { 'two', 2 } },
  2529. }
  2530. eq({
  2531. { NIL },
  2532. {
  2533. 1,
  2534. error_types.Exception.id,
  2535. 'Wrong number of arguments: expecting 5 but got 0',
  2536. },
  2537. }, api.nvim_call_atomic(req))
  2538. eq(1, api.nvim_get_var('one'))
  2539. eq(false, pcall(api.nvim_get_var, 'two'))
  2540. -- still returns all previous successful calls
  2541. req = {
  2542. { 'nvim_set_var', { 'avar', 5 } },
  2543. { 'nvim_set_var', { 'bvar', 'string' } },
  2544. { 'nvim_get_var', { 'avar' } },
  2545. { 'nvim_buf_get_lines', { 0, 10, 20, true } },
  2546. { 'nvim_get_var', { 'bvar' } },
  2547. }
  2548. eq(
  2549. { { NIL, NIL, 5 }, { 3, error_types.Validation.id, 'Index out of bounds' } },
  2550. api.nvim_call_atomic(req)
  2551. )
  2552. req = {
  2553. { 'i_am_not_a_method', { 'xx' } },
  2554. { 'nvim_set_var', { 'avar', 10 } },
  2555. }
  2556. eq(
  2557. { {}, { 0, error_types.Exception.id, 'Invalid method: i_am_not_a_method' } },
  2558. api.nvim_call_atomic(req)
  2559. )
  2560. eq(5, api.nvim_get_var('avar'))
  2561. end)
  2562. it('validation', function()
  2563. local req = {
  2564. { 'nvim_set_var', { 'avar', 1 } },
  2565. { 'nvim_set_var' },
  2566. { 'nvim_set_var', { 'avar', 2 } },
  2567. }
  2568. eq("Invalid 'calls' item: expected 2-item Array", pcall_err(api.nvim_call_atomic, req))
  2569. -- call before was done, but not after
  2570. eq(1, api.nvim_get_var('avar'))
  2571. req = {
  2572. { 'nvim_set_var', { 'bvar', { 2, 3 } } },
  2573. 12,
  2574. }
  2575. eq("Invalid 'calls' item: expected Array, got Integer", pcall_err(api.nvim_call_atomic, req))
  2576. eq({ 2, 3 }, api.nvim_get_var('bvar'))
  2577. req = {
  2578. { 'nvim_set_current_line', 'little line' },
  2579. { 'nvim_set_var', { 'avar', 3 } },
  2580. }
  2581. eq('Invalid call args: expected Array, got String', pcall_err(api.nvim_call_atomic, req))
  2582. -- call before was done, but not after
  2583. eq(1, api.nvim_get_var('avar'))
  2584. eq({ '' }, api.nvim_buf_get_lines(0, 0, -1, true))
  2585. end)
  2586. end)
  2587. describe('nvim_list_runtime_paths', function()
  2588. setup(function()
  2589. local pathsep = n.get_pathsep()
  2590. mkdir_p('Xtest' .. pathsep .. 'a')
  2591. mkdir_p('Xtest' .. pathsep .. 'b')
  2592. end)
  2593. teardown(function()
  2594. rmdir 'Xtest'
  2595. end)
  2596. before_each(function()
  2597. api.nvim_set_current_dir 'Xtest'
  2598. end)
  2599. it('returns nothing with empty &runtimepath', function()
  2600. api.nvim_set_option_value('runtimepath', '', {})
  2601. eq({}, api.nvim_list_runtime_paths())
  2602. end)
  2603. it('returns single runtimepath', function()
  2604. api.nvim_set_option_value('runtimepath', 'a', {})
  2605. eq({ 'a' }, api.nvim_list_runtime_paths())
  2606. end)
  2607. it('returns two runtimepaths', function()
  2608. api.nvim_set_option_value('runtimepath', 'a,b', {})
  2609. eq({ 'a', 'b' }, api.nvim_list_runtime_paths())
  2610. end)
  2611. it('returns empty strings when appropriate', function()
  2612. api.nvim_set_option_value('runtimepath', 'a,,b', {})
  2613. eq({ 'a', '', 'b' }, api.nvim_list_runtime_paths())
  2614. api.nvim_set_option_value('runtimepath', ',a,b', {})
  2615. eq({ '', 'a', 'b' }, api.nvim_list_runtime_paths())
  2616. -- Trailing "," is ignored. Use ",," if you really really want CWD.
  2617. api.nvim_set_option_value('runtimepath', 'a,b,', {})
  2618. eq({ 'a', 'b' }, api.nvim_list_runtime_paths())
  2619. api.nvim_set_option_value('runtimepath', 'a,b,,', {})
  2620. eq({ 'a', 'b', '' }, api.nvim_list_runtime_paths())
  2621. end)
  2622. it('truncates too long paths', function()
  2623. local long_path = ('/a'):rep(8192)
  2624. api.nvim_set_option_value('runtimepath', long_path, {})
  2625. local paths_list = api.nvim_list_runtime_paths()
  2626. eq({}, paths_list)
  2627. end)
  2628. end)
  2629. it('can throw exceptions', function()
  2630. local status, err = pcall(api.nvim_get_option_value, 'invalid-option', {})
  2631. eq(false, status)
  2632. matches("Unknown option 'invalid%-option'", err)
  2633. end)
  2634. it('does not truncate error message <1 MB #5984', function()
  2635. local very_long_name = 'A' .. ('x'):rep(10000) .. 'Z'
  2636. local status, err = pcall(api.nvim_get_option_value, very_long_name, {})
  2637. eq(false, status)
  2638. eq(very_long_name, err:match('Ax+Z?'))
  2639. end)
  2640. it('does not leak memory on incorrect argument types', function()
  2641. local status, err = pcall(api.nvim_set_current_dir, { 'not', 'a', 'dir' })
  2642. eq(false, status)
  2643. matches(': Wrong type for argument 1 when calling nvim_set_current_dir, expecting String', err)
  2644. end)
  2645. describe('nvim_parse_expression', function()
  2646. before_each(function()
  2647. api.nvim_set_option_value('isident', '', {})
  2648. end)
  2649. local function simplify_east_api_node(line, east_api_node)
  2650. if east_api_node == NIL then
  2651. return nil
  2652. end
  2653. if east_api_node.children then
  2654. for k, v in pairs(east_api_node.children) do
  2655. east_api_node.children[k] = simplify_east_api_node(line, v)
  2656. end
  2657. end
  2658. local typ = east_api_node.type
  2659. if typ == 'Register' then
  2660. typ = typ .. ('(name=%s)'):format(tostring(intchar2lua(east_api_node.name)))
  2661. east_api_node.name = nil
  2662. elseif typ == 'PlainIdentifier' then
  2663. typ = typ
  2664. .. ('(scope=%s,ident=%s)'):format(
  2665. tostring(intchar2lua(east_api_node.scope)),
  2666. east_api_node.ident
  2667. )
  2668. east_api_node.scope = nil
  2669. east_api_node.ident = nil
  2670. elseif typ == 'PlainKey' then
  2671. typ = typ .. ('(key=%s)'):format(east_api_node.ident)
  2672. east_api_node.ident = nil
  2673. elseif typ == 'Comparison' then
  2674. typ = typ
  2675. .. ('(type=%s,inv=%u,ccs=%s)'):format(
  2676. east_api_node.cmp_type,
  2677. east_api_node.invert and 1 or 0,
  2678. east_api_node.ccs_strategy
  2679. )
  2680. east_api_node.ccs_strategy = nil
  2681. east_api_node.cmp_type = nil
  2682. east_api_node.invert = nil
  2683. elseif typ == 'Integer' then
  2684. typ = typ .. ('(val=%u)'):format(east_api_node.ivalue)
  2685. east_api_node.ivalue = nil
  2686. elseif typ == 'Float' then
  2687. typ = typ .. format_string('(val=%e)', east_api_node.fvalue)
  2688. east_api_node.fvalue = nil
  2689. elseif typ == 'SingleQuotedString' or typ == 'DoubleQuotedString' then
  2690. typ = format_string('%s(val=%q)', typ, east_api_node.svalue)
  2691. east_api_node.svalue = nil
  2692. elseif typ == 'Option' then
  2693. typ = ('%s(scope=%s,ident=%s)'):format(
  2694. typ,
  2695. tostring(intchar2lua(east_api_node.scope)),
  2696. east_api_node.ident
  2697. )
  2698. east_api_node.ident = nil
  2699. east_api_node.scope = nil
  2700. elseif typ == 'Environment' then
  2701. typ = ('%s(ident=%s)'):format(typ, east_api_node.ident)
  2702. east_api_node.ident = nil
  2703. elseif typ == 'Assignment' then
  2704. local aug = east_api_node.augmentation
  2705. if aug == '' then
  2706. aug = 'Plain'
  2707. end
  2708. typ = ('%s(%s)'):format(typ, aug)
  2709. east_api_node.augmentation = nil
  2710. end
  2711. typ = ('%s:%u:%u:%s'):format(
  2712. typ,
  2713. east_api_node.start[1],
  2714. east_api_node.start[2],
  2715. line:sub(east_api_node.start[2] + 1, east_api_node.start[2] + 1 + east_api_node.len - 1)
  2716. )
  2717. assert(east_api_node.start[2] + east_api_node.len - 1 <= #line)
  2718. for k, _ in pairs(east_api_node.start) do
  2719. assert(({ true, true })[k])
  2720. end
  2721. east_api_node.start = nil
  2722. east_api_node.type = nil
  2723. east_api_node.len = nil
  2724. local can_simplify = true
  2725. for _, _ in pairs(east_api_node) do
  2726. if can_simplify then
  2727. can_simplify = false
  2728. end
  2729. end
  2730. if can_simplify then
  2731. return typ
  2732. else
  2733. east_api_node[1] = typ
  2734. return east_api_node
  2735. end
  2736. end
  2737. local function simplify_east_api(line, east_api)
  2738. if east_api.error then
  2739. east_api.err = east_api.error
  2740. east_api.error = nil
  2741. east_api.err.msg = east_api.err.message
  2742. east_api.err.message = nil
  2743. end
  2744. if east_api.ast then
  2745. east_api.ast = { simplify_east_api_node(line, east_api.ast) }
  2746. if #east_api.ast == 0 then
  2747. east_api.ast = nil
  2748. end
  2749. end
  2750. if east_api.len == #line then
  2751. east_api.len = nil
  2752. end
  2753. return east_api
  2754. end
  2755. local function simplify_east_hl(line, east_hl)
  2756. for i, v in ipairs(east_hl) do
  2757. east_hl[i] = ('%s:%u:%u:%s'):format(v[4], v[1], v[2], line:sub(v[2] + 1, v[3]))
  2758. end
  2759. return east_hl
  2760. end
  2761. local FLAGS_TO_STR = {
  2762. [0] = '',
  2763. [1] = 'm',
  2764. [2] = 'E',
  2765. [3] = 'mE',
  2766. [4] = 'l',
  2767. [5] = 'lm',
  2768. [6] = 'lE',
  2769. [7] = 'lmE',
  2770. }
  2771. local function _check_parsing(opts, str, exp_ast, exp_highlighting_fs, nz_flags_exps)
  2772. if type(str) ~= 'string' then
  2773. return
  2774. end
  2775. local zflags = opts.flags[1]
  2776. nz_flags_exps = nz_flags_exps or {}
  2777. for _, flags in ipairs(opts.flags) do
  2778. local err, msg = pcall(function()
  2779. local east_api = api.nvim_parse_expression(str, FLAGS_TO_STR[flags], true)
  2780. local east_hl = east_api.highlight
  2781. east_api.highlight = nil
  2782. local ast = simplify_east_api(str, east_api)
  2783. local hls = simplify_east_hl(str, east_hl)
  2784. local exps = {
  2785. ast = exp_ast,
  2786. hl_fs = exp_highlighting_fs,
  2787. }
  2788. local add_exps = nz_flags_exps[flags]
  2789. if not add_exps and flags == 3 + zflags then
  2790. add_exps = nz_flags_exps[1 + zflags] or nz_flags_exps[2 + zflags]
  2791. end
  2792. if add_exps then
  2793. if add_exps.ast then
  2794. exps.ast = mergedicts_copy(exps.ast, add_exps.ast)
  2795. end
  2796. if add_exps.hl_fs then
  2797. exps.hl_fs = mergedicts_copy(exps.hl_fs, add_exps.hl_fs)
  2798. end
  2799. end
  2800. eq(exps.ast, ast)
  2801. if exp_highlighting_fs then
  2802. local exp_highlighting = {}
  2803. local next_col = 0
  2804. for i, h in ipairs(exps.hl_fs) do
  2805. exp_highlighting[i], next_col = h(next_col)
  2806. end
  2807. eq(exp_highlighting, hls)
  2808. end
  2809. end)
  2810. if not err then
  2811. if type(msg) == 'table' then
  2812. local merr, new_msg = pcall(format_string, 'table error:\n%s\n\n(%r)', msg.message, msg)
  2813. if merr then
  2814. msg = new_msg
  2815. else
  2816. msg = format_string('table error without .message:\n(%r)', msg)
  2817. end
  2818. elseif type(msg) ~= 'string' then
  2819. msg = format_string('non-string non-table error:\n%r', msg)
  2820. end
  2821. error(
  2822. format_string(
  2823. 'Error while processing test (%r, %s):\n%s',
  2824. str,
  2825. FLAGS_TO_STR[flags],
  2826. msg
  2827. )
  2828. )
  2829. end
  2830. end
  2831. end
  2832. local function hl(group, str, shift)
  2833. return function(next_col)
  2834. local col = next_col + (shift or 0)
  2835. return (('%s:%u:%u:%s'):format('Nvim' .. group, 0, col, str)), (col + #str)
  2836. end
  2837. end
  2838. local function fmtn(typ, args, rest)
  2839. if
  2840. typ == 'UnknownFigure'
  2841. or typ == 'DictLiteral'
  2842. or typ == 'CurlyBracesIdentifier'
  2843. or typ == 'Lambda'
  2844. then
  2845. return ('%s%s'):format(typ, rest)
  2846. elseif typ == 'DoubleQuotedString' or typ == 'SingleQuotedString' then
  2847. if args:sub(-4) == 'NULL' then
  2848. args = args:sub(1, -5) .. '""'
  2849. end
  2850. return ('%s(%s)%s'):format(typ, args, rest)
  2851. end
  2852. end
  2853. it('does not crash parsing invalid VimL expression #29648', function()
  2854. api.nvim_input(':<C-r>=')
  2855. api.nvim_input('1bork/')
  2856. assert_alive()
  2857. end)
  2858. require('test.unit.viml.expressions.parser_tests')(it, _check_parsing, hl, fmtn)
  2859. end)
  2860. describe('nvim_list_uis', function()
  2861. it('returns empty if --headless', function()
  2862. -- Test runner defaults to --headless.
  2863. eq({}, api.nvim_list_uis())
  2864. end)
  2865. it('returns attached UIs', function()
  2866. local screen = Screen.new(20, 4, { override = true })
  2867. local expected = {
  2868. {
  2869. chan = 1,
  2870. ext_cmdline = false,
  2871. ext_hlstate = false,
  2872. ext_linegrid = screen._options.ext_linegrid or false,
  2873. ext_messages = false,
  2874. ext_multigrid = false,
  2875. ext_popupmenu = false,
  2876. ext_tabline = false,
  2877. ext_termcolors = false,
  2878. ext_wildmenu = false,
  2879. height = 4,
  2880. override = true,
  2881. rgb = true,
  2882. stdin_tty = false,
  2883. stdout_tty = false,
  2884. term_background = '',
  2885. term_colors = 0,
  2886. term_name = '',
  2887. width = 20,
  2888. },
  2889. }
  2890. eq(expected, api.nvim_list_uis())
  2891. screen:detach()
  2892. screen = Screen.new(44, 99, { rgb = false }) -- luacheck: ignore
  2893. expected[1].rgb = false
  2894. expected[1].override = false
  2895. expected[1].width = 44
  2896. expected[1].height = 99
  2897. eq(expected, api.nvim_list_uis())
  2898. end)
  2899. end)
  2900. describe('nvim_create_namespace', function()
  2901. it('works', function()
  2902. eq({}, api.nvim_get_namespaces())
  2903. eq(1, api.nvim_create_namespace('ns-1'))
  2904. eq(2, api.nvim_create_namespace('ns-2'))
  2905. eq(1, api.nvim_create_namespace('ns-1'))
  2906. eq({ ['ns-1'] = 1, ['ns-2'] = 2 }, api.nvim_get_namespaces())
  2907. eq(3, api.nvim_create_namespace(''))
  2908. eq(4, api.nvim_create_namespace(''))
  2909. eq({ ['ns-1'] = 1, ['ns-2'] = 2 }, api.nvim_get_namespaces())
  2910. end)
  2911. end)
  2912. describe('nvim_create_buf', function()
  2913. it('works', function()
  2914. eq(2, api.nvim_create_buf(true, false))
  2915. eq(3, api.nvim_create_buf(false, false))
  2916. eq(
  2917. ' 1 %a "[No Name]" line 1\n'
  2918. .. ' 2 h "[No Name]" line 0',
  2919. command_output('ls')
  2920. )
  2921. -- current buffer didn't change
  2922. eq(1, api.nvim_get_current_buf())
  2923. local screen = Screen.new(20, 4)
  2924. api.nvim_buf_set_lines(2, 0, -1, true, { 'some text' })
  2925. api.nvim_set_current_buf(2)
  2926. screen:expect(
  2927. [[
  2928. ^some text |
  2929. {1:~ }|*2
  2930. |
  2931. ]],
  2932. {
  2933. [1] = { bold = true, foreground = Screen.colors.Blue1 },
  2934. }
  2935. )
  2936. end)
  2937. it('can change buftype before visiting', function()
  2938. api.nvim_set_option_value('hidden', false, {})
  2939. eq(2, api.nvim_create_buf(true, false))
  2940. api.nvim_set_option_value('buftype', 'nofile', { buf = 2 })
  2941. api.nvim_buf_set_lines(2, 0, -1, true, { 'test text' })
  2942. command('split | buffer 2')
  2943. eq(2, api.nvim_get_current_buf())
  2944. -- if the buf_set_option("buftype") didn't work, this would error out.
  2945. command('close')
  2946. eq(1, api.nvim_get_current_buf())
  2947. end)
  2948. it('does not trigger BufEnter, BufWinEnter', function()
  2949. command('let g:fired = v:false')
  2950. command('au BufEnter,BufWinEnter * let g:fired = v:true')
  2951. eq(2, api.nvim_create_buf(true, false))
  2952. api.nvim_buf_set_lines(2, 0, -1, true, { 'test', 'text' })
  2953. eq(false, eval('g:fired'))
  2954. end)
  2955. it('TextChanged and TextChangedI do not trigger without changes', function()
  2956. local buf = api.nvim_create_buf(true, false)
  2957. command([[let g:changed = '']])
  2958. api.nvim_create_autocmd({ 'TextChanged', 'TextChangedI' }, {
  2959. buffer = buf,
  2960. command = 'let g:changed ..= mode()',
  2961. })
  2962. api.nvim_set_current_buf(buf)
  2963. feed('i')
  2964. eq('', api.nvim_get_var('changed'))
  2965. end)
  2966. it('scratch-buffer', function()
  2967. eq(2, api.nvim_create_buf(false, true))
  2968. eq(3, api.nvim_create_buf(true, true))
  2969. eq(4, api.nvim_create_buf(true, true))
  2970. local scratch_bufs = { 2, 3, 4 }
  2971. eq(
  2972. ' 1 %a "[No Name]" line 1\n'
  2973. .. ' 3 h "[Scratch]" line 0\n'
  2974. .. ' 4 h "[Scratch]" line 0',
  2975. exec_capture('ls')
  2976. )
  2977. -- current buffer didn't change
  2978. eq(1, api.nvim_get_current_buf())
  2979. local screen = Screen.new(20, 4)
  2980. --
  2981. -- Editing a scratch-buffer does NOT change its properties.
  2982. --
  2983. local edited_buf = 2
  2984. api.nvim_buf_set_lines(edited_buf, 0, -1, true, { 'some text' })
  2985. for _, b in ipairs(scratch_bufs) do
  2986. eq('nofile', api.nvim_get_option_value('buftype', { buf = b }))
  2987. eq('hide', api.nvim_get_option_value('bufhidden', { buf = b }))
  2988. eq(false, api.nvim_get_option_value('swapfile', { buf = b }))
  2989. eq(false, api.nvim_get_option_value('modeline', { buf = b }))
  2990. end
  2991. --
  2992. -- Visiting a scratch-buffer DOES NOT change its properties.
  2993. --
  2994. api.nvim_set_current_buf(edited_buf)
  2995. screen:expect([[
  2996. ^some text |
  2997. {1:~ }|*2
  2998. |
  2999. ]])
  3000. eq('nofile', api.nvim_get_option_value('buftype', { buf = edited_buf }))
  3001. eq('hide', api.nvim_get_option_value('bufhidden', { buf = edited_buf }))
  3002. eq(false, api.nvim_get_option_value('swapfile', { buf = edited_buf }))
  3003. eq(false, api.nvim_get_option_value('modeline', { buf = edited_buf }))
  3004. -- Scratch buffer can be wiped without error.
  3005. command('bwipe')
  3006. screen:expect([[
  3007. ^ |
  3008. {1:~ }|*2
  3009. |
  3010. ]])
  3011. end)
  3012. it('does not cause heap-use-after-free on exit while setting options', function()
  3013. command('au OptionSet * q')
  3014. command('silent! call nvim_create_buf(0, 1)')
  3015. -- nowadays this works because we don't execute any spurious autocmds at all #24824
  3016. assert_alive()
  3017. end)
  3018. it('no memory leak when autocommands load the buffer immediately', function()
  3019. exec([[
  3020. autocmd BufNew * ++once call bufload(expand("<abuf>")->str2nr())
  3021. \| let loaded = bufloaded(expand("<abuf>")->str2nr())
  3022. ]])
  3023. api.nvim_create_buf(false, true)
  3024. eq(1, eval('g:loaded'))
  3025. end)
  3026. it('creating scratch buffer where autocommands set &swapfile works', function()
  3027. exec([[
  3028. autocmd BufNew * ++once execute expand("<abuf>") "buffer"
  3029. \| file foobar
  3030. \| setlocal swapfile
  3031. ]])
  3032. local new_buf = api.nvim_create_buf(false, true)
  3033. neq('', fn.swapname(new_buf))
  3034. end)
  3035. it('fires expected autocommands', function()
  3036. exec([=[
  3037. " Append the &buftype to check autocommands trigger *after* the buffer was configured to be
  3038. " scratch, if applicable.
  3039. autocmd BufNew * let fired += [["BufNew", expand("<abuf>")->str2nr(),
  3040. \ getbufvar(expand("<abuf>")->str2nr(), "&buftype")]]
  3041. autocmd BufAdd * let fired += [["BufAdd", expand("<abuf>")->str2nr(),
  3042. \ getbufvar(expand("<abuf>")->str2nr(), "&buftype")]]
  3043. " Don't want to see OptionSet; buffer options set from passing true for "scratch", etc.
  3044. " should be configured invisibly, and before autocommands.
  3045. autocmd OptionSet * let fired += [["OptionSet", expand("<amatch>")]]
  3046. let fired = []
  3047. ]=])
  3048. local new_buf = api.nvim_create_buf(false, false)
  3049. eq({ { 'BufNew', new_buf, '' } }, eval('g:fired'))
  3050. command('let fired = []')
  3051. new_buf = api.nvim_create_buf(false, true)
  3052. eq({ { 'BufNew', new_buf, 'nofile' } }, eval('g:fired'))
  3053. command('let fired = []')
  3054. new_buf = api.nvim_create_buf(true, false)
  3055. eq({ { 'BufNew', new_buf, '' }, { 'BufAdd', new_buf, '' } }, eval('g:fired'))
  3056. command('let fired = []')
  3057. new_buf = api.nvim_create_buf(true, true)
  3058. eq({ { 'BufNew', new_buf, 'nofile' }, { 'BufAdd', new_buf, 'nofile' } }, eval('g:fired'))
  3059. end)
  3060. end)
  3061. describe('nvim_get_runtime_file', function()
  3062. local p = t.fix_slashes
  3063. it('can find files', function()
  3064. eq({}, api.nvim_get_runtime_file('bork.borkbork', false))
  3065. eq({}, api.nvim_get_runtime_file('bork.borkbork', true))
  3066. eq(1, #api.nvim_get_runtime_file('autoload/msgpack.vim', false))
  3067. eq(1, #api.nvim_get_runtime_file('autoload/msgpack.vim', true))
  3068. local val = api.nvim_get_runtime_file('autoload/remote/*.vim', true)
  3069. eq(2, #val)
  3070. if endswith(val[1], 'define.vim') then
  3071. ok(endswith(p(val[1]), 'autoload/remote/define.vim'))
  3072. ok(endswith(p(val[2]), 'autoload/remote/host.vim'))
  3073. else
  3074. ok(endswith(p(val[1]), 'autoload/remote/host.vim'))
  3075. ok(endswith(p(val[2]), 'autoload/remote/define.vim'))
  3076. end
  3077. val = api.nvim_get_runtime_file('autoload/remote/*.vim', false)
  3078. eq(1, #val)
  3079. ok(
  3080. endswith(p(val[1]), 'autoload/remote/define.vim')
  3081. or endswith(p(val[1]), 'autoload/remote/host.vim')
  3082. )
  3083. val = api.nvim_get_runtime_file('lua', true)
  3084. eq(1, #val)
  3085. ok(endswith(p(val[1]), 'lua'))
  3086. val = api.nvim_get_runtime_file('lua/vim', true)
  3087. eq(1, #val)
  3088. ok(endswith(p(val[1]), 'lua/vim'))
  3089. end)
  3090. it('can find directories', function()
  3091. local val = api.nvim_get_runtime_file('lua/', true)
  3092. eq(1, #val)
  3093. ok(endswith(p(val[1]), 'lua/'))
  3094. val = api.nvim_get_runtime_file('lua/vim/', true)
  3095. eq(1, #val)
  3096. ok(endswith(p(val[1]), 'lua/vim/'))
  3097. eq({}, api.nvim_get_runtime_file('foobarlang/', true))
  3098. end)
  3099. it('can handle bad patterns', function()
  3100. skip(is_os('win'))
  3101. eq('Vim:E220: Missing }.', pcall_err(api.nvim_get_runtime_file, '{', false))
  3102. eq(
  3103. 'Vim(echo):E5555: API call: Vim:E220: Missing }.',
  3104. exc_exec("echo nvim_get_runtime_file('{', v:false)")
  3105. )
  3106. end)
  3107. it('preserves order of runtimepath', function()
  3108. local vimruntime = fn.getenv('VIMRUNTIME')
  3109. local rtp = string.format('%s/syntax,%s/ftplugin', vimruntime, vimruntime)
  3110. api.nvim_set_option_value('runtimepath', rtp, {})
  3111. local val = api.nvim_get_runtime_file('vim.vim', true)
  3112. eq(2, #val)
  3113. eq(p(val[1]), vimruntime .. '/syntax/vim.vim')
  3114. eq(p(val[2]), vimruntime .. '/ftplugin/vim.vim')
  3115. end)
  3116. end)
  3117. describe('nvim_get_all_options_info', function()
  3118. it('should have key value pairs of option names', function()
  3119. local options_info = api.nvim_get_all_options_info()
  3120. neq(nil, options_info.listchars)
  3121. neq(nil, options_info.tabstop)
  3122. eq(api.nvim_get_option_info 'winhighlight', options_info.winhighlight)
  3123. end)
  3124. it('should not crash when echoed', function()
  3125. api.nvim_exec2('echo nvim_get_all_options_info()', { output = true })
  3126. end)
  3127. end)
  3128. describe('nvim_get_option_info', function()
  3129. it('should error for unknown options', function()
  3130. eq("Invalid option (not found): 'bogus'", pcall_err(api.nvim_get_option_info, 'bogus'))
  3131. end)
  3132. it('should return the same options for short and long name', function()
  3133. eq(api.nvim_get_option_info 'winhl', api.nvim_get_option_info 'winhighlight')
  3134. end)
  3135. it('should have information about window options', function()
  3136. eq({
  3137. allows_duplicates = false,
  3138. commalist = true,
  3139. default = '',
  3140. flaglist = false,
  3141. global_local = false,
  3142. last_set_chan = 0,
  3143. last_set_linenr = 0,
  3144. last_set_sid = 0,
  3145. name = 'winhighlight',
  3146. scope = 'win',
  3147. shortname = 'winhl',
  3148. type = 'string',
  3149. was_set = false,
  3150. }, api.nvim_get_option_info 'winhl')
  3151. end)
  3152. it('should have information about buffer options', function()
  3153. eq({
  3154. allows_duplicates = true,
  3155. commalist = false,
  3156. default = '',
  3157. flaglist = false,
  3158. global_local = false,
  3159. last_set_chan = 0,
  3160. last_set_linenr = 0,
  3161. last_set_sid = 0,
  3162. name = 'filetype',
  3163. scope = 'buf',
  3164. shortname = 'ft',
  3165. type = 'string',
  3166. was_set = false,
  3167. }, api.nvim_get_option_info 'filetype')
  3168. end)
  3169. it('should have information about global options', function()
  3170. -- precondition: the option was changed from its default
  3171. -- in test setup.
  3172. eq(false, api.nvim_get_option_value('showcmd', {}))
  3173. eq({
  3174. allows_duplicates = true,
  3175. commalist = false,
  3176. default = true,
  3177. flaglist = false,
  3178. global_local = false,
  3179. last_set_chan = 0,
  3180. last_set_linenr = 0,
  3181. last_set_sid = -2,
  3182. name = 'showcmd',
  3183. scope = 'global',
  3184. shortname = 'sc',
  3185. type = 'boolean',
  3186. was_set = true,
  3187. }, api.nvim_get_option_info 'showcmd')
  3188. api.nvim_set_option_value('showcmd', true, {})
  3189. eq({
  3190. allows_duplicates = true,
  3191. commalist = false,
  3192. default = true,
  3193. flaglist = false,
  3194. global_local = false,
  3195. last_set_chan = 1,
  3196. last_set_linenr = 0,
  3197. last_set_sid = -9,
  3198. name = 'showcmd',
  3199. scope = 'global',
  3200. shortname = 'sc',
  3201. type = 'boolean',
  3202. was_set = true,
  3203. }, api.nvim_get_option_info 'showcmd')
  3204. end)
  3205. end)
  3206. describe('nvim_get_option_info2', function()
  3207. local fname
  3208. local bufs
  3209. local wins
  3210. before_each(function()
  3211. fname = tmpname()
  3212. write_file(
  3213. fname,
  3214. [[
  3215. setglobal dictionary=mydict " 1, global-local (buffer)
  3216. setlocal formatprg=myprg " 2, global-local (buffer)
  3217. setglobal equalprg=prg1 " 3, global-local (buffer)
  3218. setlocal equalprg=prg2 " 4, global-local (buffer)
  3219. setglobal fillchars=stl:x " 5, global-local (window)
  3220. setlocal listchars=eol:c " 6, global-local (window)
  3221. setglobal showbreak=aaa " 7, global-local (window)
  3222. setlocal showbreak=bbb " 8, global-local (window)
  3223. setglobal completeopt=menu " 9, global
  3224. ]]
  3225. )
  3226. exec_lua 'vim.cmd.vsplit()'
  3227. api.nvim_create_buf(false, false)
  3228. bufs = api.nvim_list_bufs()
  3229. wins = api.nvim_list_wins()
  3230. api.nvim_win_set_buf(wins[1], bufs[1])
  3231. api.nvim_win_set_buf(wins[2], bufs[2])
  3232. api.nvim_set_current_win(wins[2])
  3233. api.nvim_exec('source ' .. fname, false)
  3234. api.nvim_set_current_win(wins[1])
  3235. end)
  3236. after_each(function()
  3237. os.remove(fname)
  3238. end)
  3239. it('should return option information', function()
  3240. eq(api.nvim_get_option_info('dictionary'), api.nvim_get_option_info2('dictionary', {})) -- buffer
  3241. eq(api.nvim_get_option_info('fillchars'), api.nvim_get_option_info2('fillchars', {})) -- window
  3242. eq(api.nvim_get_option_info('completeopt'), api.nvim_get_option_info2('completeopt', {})) -- global
  3243. end)
  3244. describe('last set', function()
  3245. -- stylua: ignore
  3246. local tests = {
  3247. {desc="(buf option, global requested, global set) points to global", linenr=1, sid=1, args={'dictionary', {scope='global'}}},
  3248. {desc="(buf option, global requested, local set) is not set", linenr=0, sid=0, args={'formatprg', {scope='global'}}},
  3249. {desc="(buf option, global requested, both set) points to global", linenr=3, sid=1, args={'equalprg', {scope='global'}}},
  3250. {desc="(buf option, local requested, global set) is not set", linenr=0, sid=0, args={'dictionary', {scope='local'}}},
  3251. {desc="(buf option, local requested, local set) points to local", linenr=2, sid=1, args={'formatprg', {scope='local'}}},
  3252. {desc="(buf option, local requested, both set) points to local", linenr=4, sid=1, args={'equalprg', {scope='local'}}},
  3253. {desc="(buf option, fallback requested, global set) points to global", linenr=1, sid=1, args={'dictionary', {}}},
  3254. {desc="(buf option, fallback requested, local set) points to local", linenr=2, sid=1, args={'formatprg', {}}},
  3255. {desc="(buf option, fallback requested, both set) points to local", linenr=4, sid=1, args={'equalprg', {}}},
  3256. {desc="(win option, global requested, global set) points to global", linenr=5, sid=1, args={'fillchars', {scope='global'}}},
  3257. {desc="(win option, global requested, local set) is not set", linenr=0, sid=0, args={'listchars', {scope='global'}}},
  3258. {desc="(win option, global requested, both set) points to global", linenr=7, sid=1, args={'showbreak', {scope='global'}}},
  3259. {desc="(win option, local requested, global set) is not set", linenr=0, sid=0, args={'fillchars', {scope='local'}}},
  3260. {desc="(win option, local requested, local set) points to local", linenr=6, sid=1, args={'listchars', {scope='local'}}},
  3261. {desc="(win option, local requested, both set) points to local", linenr=8, sid=1, args={'showbreak', {scope='local'}}},
  3262. {desc="(win option, fallback requested, global set) points to global", linenr=5, sid=1, args={'fillchars', {}}},
  3263. {desc="(win option, fallback requested, local set) points to local", linenr=6, sid=1, args={'listchars', {}}},
  3264. {desc="(win option, fallback requested, both set) points to local", linenr=8, sid=1, args={'showbreak', {}}},
  3265. {desc="(global option, global requested) points to global", linenr=9, sid=1, args={'completeopt', {scope='global'}}},
  3266. {desc="(global option, local requested) is not set", linenr=0, sid=0, args={'completeopt', {scope='local'}}},
  3267. {desc="(global option, fallback requested) points to global", linenr=9, sid=1, args={'completeopt', {}}},
  3268. }
  3269. for _, test in pairs(tests) do
  3270. it(test.desc, function()
  3271. -- Switch to the target buffer/window so that curbuf/curwin are used.
  3272. api.nvim_set_current_win(wins[2])
  3273. local info = api.nvim_get_option_info2(unpack(test.args))
  3274. eq(test.linenr, info.last_set_linenr)
  3275. eq(test.sid, info.last_set_sid)
  3276. end)
  3277. end
  3278. it('is provided for cross-buffer requests', function()
  3279. local info = api.nvim_get_option_info2('formatprg', { buf = bufs[2] })
  3280. eq(2, info.last_set_linenr)
  3281. eq(1, info.last_set_sid)
  3282. end)
  3283. it('is provided for cross-window requests', function()
  3284. local info = api.nvim_get_option_info2('listchars', { win = wins[2] })
  3285. eq(6, info.last_set_linenr)
  3286. eq(1, info.last_set_sid)
  3287. end)
  3288. end)
  3289. end)
  3290. describe('nvim_echo', function()
  3291. local screen
  3292. before_each(function()
  3293. screen = Screen.new(40, 8)
  3294. command('highlight Statement gui=bold guifg=Brown')
  3295. command('highlight Special guifg=SlateBlue')
  3296. end)
  3297. it('validation', function()
  3298. eq("Invalid 'chunk': expected Array, got String", pcall_err(api.nvim_echo, { 'msg' }, 1, {}))
  3299. eq(
  3300. 'Invalid chunk: expected Array with 1 or 2 Strings',
  3301. pcall_err(api.nvim_echo, { { '', '', '' } }, 1, {})
  3302. )
  3303. eq('Invalid hl_group: text highlight', pcall_err(api.nvim_echo, { { '', false } }, 1, {}))
  3304. end)
  3305. it('should clear cmdline message before echo', function()
  3306. feed(':call nvim_echo([["msg"]], v:false, {})<CR>')
  3307. screen:expect {
  3308. grid = [[
  3309. ^ |
  3310. {1:~ }|*6
  3311. msg |
  3312. ]],
  3313. }
  3314. end)
  3315. it('can show highlighted line', function()
  3316. async_meths.nvim_echo(
  3317. { { 'msg_a' }, { 'msg_b', 'Statement' }, { 'msg_c', 'Special' } },
  3318. true,
  3319. {}
  3320. )
  3321. screen:expect {
  3322. grid = [[
  3323. ^ |
  3324. {1:~ }|*6
  3325. msg_a{15:msg_b}{16:msg_c} |
  3326. ]],
  3327. }
  3328. async_meths.nvim_echo({
  3329. { 'msg_d' },
  3330. { 'msg_e', api.nvim_get_hl_id_by_name('Statement') },
  3331. { 'msg_f', api.nvim_get_hl_id_by_name('Special') },
  3332. }, true, {})
  3333. screen:expect {
  3334. grid = [[
  3335. ^ |
  3336. {1:~ }|*6
  3337. msg_d{15:msg_e}{16:msg_f} |
  3338. ]],
  3339. }
  3340. end)
  3341. it('can show highlighted multiline', function()
  3342. async_meths.nvim_echo({ { 'msg_a\nmsg_a', 'Statement' }, { 'msg_b', 'Special' } }, true, {})
  3343. screen:expect {
  3344. grid = [[
  3345. |
  3346. {1:~ }|*3
  3347. {3: }|
  3348. {15:msg_a} |
  3349. {15:msg_a}{16:msg_b} |
  3350. {6:Press ENTER or type command to continue}^ |
  3351. ]],
  3352. }
  3353. end)
  3354. it('can save message history', function()
  3355. command('set cmdheight=2') -- suppress Press ENTER
  3356. api.nvim_echo({ { 'msg\nmsg' }, { 'msg' } }, true, {})
  3357. eq('msg\nmsgmsg', exec_capture('messages'))
  3358. end)
  3359. it('can disable saving message history', function()
  3360. command('set cmdheight=2') -- suppress Press ENTER
  3361. async_meths.nvim_echo({ { 'msg\nmsg' }, { 'msg' } }, false, {})
  3362. eq('', exec_capture('messages'))
  3363. end)
  3364. it('can print error message', function()
  3365. async_meths.nvim_echo({ { 'Error\nMessage' } }, false, { err = true })
  3366. screen:expect([[
  3367. |
  3368. {1:~ }|*3
  3369. {3: }|
  3370. {9:Error} |
  3371. {9:Message} |
  3372. {6:Press ENTER or type command to continue}^ |
  3373. ]])
  3374. feed(':messages<CR>')
  3375. screen:expect([[
  3376. ^ |
  3377. {1:~ }|*6
  3378. |
  3379. ]])
  3380. async_meths.nvim_echo({ { 'Error' }, { 'Message', 'Special' } }, false, { err = true })
  3381. screen:expect([[
  3382. ^ |
  3383. {1:~ }|*6
  3384. {9:Error}{16:Message} |
  3385. ]])
  3386. end)
  3387. end)
  3388. describe('nvim_open_term', function()
  3389. local screen
  3390. before_each(function()
  3391. screen = Screen.new(100, 35)
  3392. screen:add_extra_attr_ids {
  3393. [100] = { background = tonumber('0xffff40'), bg_indexed = true },
  3394. [101] = {
  3395. background = Screen.colors.LightMagenta,
  3396. foreground = tonumber('0x00e000'),
  3397. fg_indexed = true,
  3398. },
  3399. [102] = { background = Screen.colors.LightMagenta, reverse = true },
  3400. [103] = { background = Screen.colors.LightMagenta, bold = true, reverse = true },
  3401. }
  3402. end)
  3403. it('can batch process sequences', function()
  3404. local b = api.nvim_create_buf(true, true)
  3405. api.nvim_open_win(
  3406. b,
  3407. false,
  3408. { width = 79, height = 31, row = 1, col = 1, relative = 'editor' }
  3409. )
  3410. local term = api.nvim_open_term(b, {})
  3411. api.nvim_chan_send(term, io.open('test/functional/fixtures/smile2.cat', 'r'):read('*a'))
  3412. screen:expect {
  3413. grid = [[
  3414. ^ |
  3415. {1:~}{4::smile }{1: }|
  3416. {1:~}{4: }{100:oooo$$$$$$$$$$$$oooo}{4: }{1: }|
  3417. {1:~}{4: }{100:oo$$$$$$$$$$$$$$$$$$$$$$$$o}{4: }{1: }|
  3418. {1:~}{4: }{100:oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{4: }{100:o$}{4: }{100:$$}{4: }{100:o$}{4: }{1: }|
  3419. {1:~}{4: }{100:o}{4: }{100:$}{4: }{100:oo}{4: }{100:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{4: }{100:$$}{4: }{100:$$}{4: }{100:$$o$}{4: }{1: }|
  3420. {1:~}{4: }{100:oo}{4: }{100:$}{4: }{100:$}{4: "}{100:$}{4: }{100:o$$$$$$$$$}{4: }{100:$$$$$$$$$$$$$}{4: }{100:$$$$$$$$$o}{4: }{100:$$$o$$o$}{4: }{1: }|
  3421. {1:~}{4: "}{100:$$$$$$o$}{4: }{100:o$$$$$$$$$}{4: }{100:$$$$$$$$$$$}{4: }{100:$$$$$$$$$$o}{4: }{100:$$$$$$$$}{4: }{1: }|
  3422. {1:~}{4: }{100:$$$$$$$}{4: }{100:$$$$$$$$$$$}{4: }{100:$$$$$$$$$$$}{4: }{100:$$$$$$$$$$$$$$$$$$$$$$$}{4: }{1: }|
  3423. {1:~}{4: }{100:$$$$$$$$$$$$$$$$$$$$$$$}{4: }{100:$$$$$$$$$$$$$}{4: }{100:$$$$$$$$$$$$$$}{4: """}{100:$$$}{4: }{1: }|
  3424. {1:~}{4: "}{100:$$$}{4:""""}{100:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{4: "}{100:$$$}{4: }{1: }|
  3425. {1:~}{4: }{100:$$$}{4: }{100:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{4: "}{100:$$$o}{4: }{1: }|
  3426. {1:~}{4: }{100:o$$}{4:" }{100:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{4: }{100:$$$o}{4: }{1: }|
  3427. {1:~}{4: }{100:$$$}{4: }{100:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{4:" "}{100:$$$$$$ooooo$$$$o}{4: }{1: }|
  3428. {1:~}{4: }{100:o$$$oooo$$$$$}{4: }{100:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{4: }{100:o$$$$$$$$$$$$$$$$$}{4: }{1: }|
  3429. {1:~}{4: }{100:$$$$$$$$}{4:"}{100:$$$$}{4: }{100:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{4: }{100:$$$$}{4:"""""""" }{1: }|
  3430. {1:~}{4: """" }{100:$$$$}{4: "}{100:$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{4:" }{100:o$$$}{4: }{1: }|
  3431. {1:~}{4: "}{100:$$$o}{4: """}{100:$$$$$$$$$$$$$$$$$$}{4:"}{100:$$}{4:" }{100:$$$}{4: }{1: }|
  3432. {1:~}{4: }{100:$$$o}{4: "}{100:$$}{4:""}{100:$$$$$$}{4:"""" }{100:o$$$}{4: }{1: }|
  3433. {1:~}{4: }{100:$$$$o}{4: }{100:o$$$}{4:" }{1: }|
  3434. {1:~}{4: "}{100:$$$$o}{4: }{100:o$$$$$$o}{4:"}{100:$$$$o}{4: }{100:o$$$$}{4: }{1: }|
  3435. {1:~}{4: "}{100:$$$$$oo}{4: ""}{100:$$$$o$$$$$o}{4: }{100:o$$$$}{4:"" }{1: }|
  3436. {1:~}{4: ""}{100:$$$$$oooo}{4: "}{100:$$$o$$$$$$$$$}{4:""" }{1: }|
  3437. {1:~}{4: ""}{100:$$$$$$$oo}{4: }{100:$$$$$$$$$$}{4: }{1: }|
  3438. {1:~}{4: """"}{100:$$$$$$$$$$$}{4: }{1: }|
  3439. {1:~}{4: }{100:$$$$$$$$$$$$}{4: }{1: }|
  3440. {1:~}{4: }{100:$$$$$$$$$$}{4:" }{1: }|
  3441. {1:~}{4: "}{100:$$$}{4:"""" }{1: }|
  3442. {1:~}{4: }{1: }|
  3443. {1:~}{101:Press ENTER or type command to continue}{4: }{1: }|
  3444. {1:~}{103:term://~/config2/docs/pres//32693:vim --clean +smile 29,39 All}{1: }|
  3445. {1:~}{4::call nvim__screenshot("smile2.cat") }{1: }|
  3446. {1:~ }|*2
  3447. |
  3448. ]],
  3449. }
  3450. end)
  3451. it('can handle input', function()
  3452. screen:try_resize(50, 10)
  3453. eq(
  3454. { 3, 2 },
  3455. exec_lua [[
  3456. buf = vim.api.nvim_create_buf(1,1)
  3457. stream = ''
  3458. do_the_echo = false
  3459. function input(_,t1,b1,data)
  3460. stream = stream .. data
  3461. _G.vals = {t1, b1}
  3462. if do_the_echo then
  3463. vim.api.nvim_chan_send(t1, data)
  3464. end
  3465. end
  3466. term = vim.api.nvim_open_term(buf, {on_input=input})
  3467. vim.api.nvim_open_win(buf, true, {width=40, height=5, row=1, col=1, relative='editor'})
  3468. return {term, buf}
  3469. ]]
  3470. )
  3471. screen:expect {
  3472. grid = [[
  3473. |
  3474. {1:~}{4:^ }{1: }|
  3475. {1:~}{4: }{1: }|*4
  3476. {1:~ }|*3
  3477. |
  3478. ]],
  3479. }
  3480. feed 'iba<c-x>bla'
  3481. screen:expect {
  3482. grid = [[
  3483. |
  3484. {1:~}{4:^ }{1: }|
  3485. {1:~}{4: }{1: }|*4
  3486. {1:~ }|*3
  3487. {5:-- TERMINAL --} |
  3488. ]],
  3489. }
  3490. eq('ba\024bla', exec_lua [[ return stream ]])
  3491. eq({ 3, 2 }, exec_lua [[ return vals ]])
  3492. exec_lua [[ do_the_echo = true ]]
  3493. feed 'herrejösses!'
  3494. screen:expect {
  3495. grid = [[
  3496. |
  3497. {1:~}{4:herrejösses!^ }{1: }|
  3498. {1:~}{4: }{1: }|*4
  3499. {1:~ }|*3
  3500. {5:-- TERMINAL --} |
  3501. ]],
  3502. }
  3503. eq('ba\024blaherrejösses!', exec_lua [[ return stream ]])
  3504. end)
  3505. end)
  3506. describe('nvim_del_mark', function()
  3507. it('works', function()
  3508. local buf = api.nvim_create_buf(false, true)
  3509. api.nvim_buf_set_lines(buf, -1, -1, true, { 'a', 'bit of', 'text' })
  3510. eq(true, api.nvim_buf_set_mark(buf, 'F', 2, 2, {}))
  3511. eq(true, api.nvim_del_mark('F'))
  3512. eq({ 0, 0 }, api.nvim_buf_get_mark(buf, 'F'))
  3513. end)
  3514. it('validation', function()
  3515. eq("Invalid mark name (must be file/uppercase): 'f'", pcall_err(api.nvim_del_mark, 'f'))
  3516. eq("Invalid mark name (must be file/uppercase): '!'", pcall_err(api.nvim_del_mark, '!'))
  3517. eq("Invalid mark name (must be a single char): 'fail'", pcall_err(api.nvim_del_mark, 'fail'))
  3518. end)
  3519. end)
  3520. describe('nvim_get_mark', function()
  3521. it('works', function()
  3522. local buf = api.nvim_create_buf(false, true)
  3523. api.nvim_buf_set_lines(buf, -1, -1, true, { 'a', 'bit of', 'text' })
  3524. api.nvim_buf_set_mark(buf, 'F', 2, 2, {})
  3525. api.nvim_buf_set_name(buf, 'mybuf')
  3526. local mark = api.nvim_get_mark('F', {})
  3527. -- Compare the path tail only
  3528. matches('mybuf$', mark[4])
  3529. eq({ 2, 2, buf, mark[4] }, mark)
  3530. end)
  3531. it('validation', function()
  3532. eq("Invalid mark name (must be file/uppercase): 'f'", pcall_err(api.nvim_get_mark, 'f', {}))
  3533. eq("Invalid mark name (must be file/uppercase): '!'", pcall_err(api.nvim_get_mark, '!', {}))
  3534. eq(
  3535. "Invalid mark name (must be a single char): 'fail'",
  3536. pcall_err(api.nvim_get_mark, 'fail', {})
  3537. )
  3538. end)
  3539. it('returns the expected when mark is not set', function()
  3540. eq(true, api.nvim_del_mark('A'))
  3541. eq({ 0, 0, 0, '' }, api.nvim_get_mark('A', {}))
  3542. end)
  3543. it('works with deleted buffers', function()
  3544. local fname = tmpname()
  3545. write_file(fname, 'a\nbit of\text')
  3546. command('edit ' .. fname)
  3547. local buf = api.nvim_get_current_buf()
  3548. api.nvim_buf_set_mark(buf, 'F', 2, 2, {})
  3549. command('new') -- Create new buf to avoid :bd failing
  3550. command('bd! ' .. buf)
  3551. os.remove(fname)
  3552. local mark = api.nvim_get_mark('F', {})
  3553. -- To avoid comparing relative vs absolute path
  3554. local mfname = mark[4]
  3555. local tail_patt = [[[\/][^\/]*$]]
  3556. -- tail of paths should be equals
  3557. eq(fname:match(tail_patt), mfname:match(tail_patt))
  3558. eq({ 2, 2, buf, mark[4] }, mark)
  3559. end)
  3560. end)
  3561. describe('nvim_eval_statusline', function()
  3562. it('works', function()
  3563. eq({
  3564. str = '%StatusLineStringWithHighlights',
  3565. width = 31,
  3566. }, api.nvim_eval_statusline('%%StatusLineString%#WarningMsg#WithHighlights', {}))
  3567. end)
  3568. it("doesn't exceed maxwidth", function()
  3569. eq({
  3570. str = 'Should be trun>',
  3571. width = 15,
  3572. }, api.nvim_eval_statusline('Should be truncated%<', { maxwidth = 15 }))
  3573. end)
  3574. it('has correct default fillchar', function()
  3575. local oldwin = api.nvim_get_current_win()
  3576. command('set fillchars=stl:#,stlnc:$,wbr:%')
  3577. command('new')
  3578. eq({ str = 'a###b', width = 5 }, api.nvim_eval_statusline('a%=b', { maxwidth = 5 }))
  3579. eq(
  3580. { str = 'a$$$b', width = 5 },
  3581. api.nvim_eval_statusline('a%=b', { winid = oldwin, maxwidth = 5 })
  3582. )
  3583. eq(
  3584. { str = 'a%%%b', width = 5 },
  3585. api.nvim_eval_statusline('a%=b', { use_winbar = true, maxwidth = 5 })
  3586. )
  3587. eq(
  3588. { str = 'a b', width = 5 },
  3589. api.nvim_eval_statusline('a%=b', { use_tabline = true, maxwidth = 5 })
  3590. )
  3591. eq(
  3592. { str = 'a b', width = 5 },
  3593. api.nvim_eval_statusline('a%=b', { use_statuscol_lnum = 1, maxwidth = 5 })
  3594. )
  3595. end)
  3596. for fc, desc in pairs({
  3597. ['~'] = 'supports ASCII fillchar',
  3598. ['━'] = 'supports single-width multibyte fillchar',
  3599. ['c̳'] = 'supports single-width fillchar with composing',
  3600. ['哦'] = 'treats double-width fillchar as single-width',
  3601. ['\031'] = 'treats control character fillchar as single-width',
  3602. }) do
  3603. it(desc, function()
  3604. eq(
  3605. { str = 'a' .. fc:rep(3) .. 'b', width = 5 },
  3606. api.nvim_eval_statusline('a%=b', { fillchar = fc, maxwidth = 5 })
  3607. )
  3608. eq(
  3609. { str = 'a' .. fc:rep(3) .. 'b', width = 5 },
  3610. api.nvim_eval_statusline('a%=b', { fillchar = fc, use_winbar = true, maxwidth = 5 })
  3611. )
  3612. eq(
  3613. { str = 'a' .. fc:rep(3) .. 'b', width = 5 },
  3614. api.nvim_eval_statusline('a%=b', { fillchar = fc, use_tabline = true, maxwidth = 5 })
  3615. )
  3616. eq(
  3617. { str = 'a' .. fc:rep(3) .. 'b', width = 5 },
  3618. api.nvim_eval_statusline('a%=b', { fillchar = fc, use_statuscol_lnum = 1, maxwidth = 5 })
  3619. )
  3620. end)
  3621. end
  3622. it('rejects multiple-character fillchar', function()
  3623. eq(
  3624. "Invalid 'fillchar': expected single character",
  3625. pcall_err(api.nvim_eval_statusline, '', { fillchar = 'aa' })
  3626. )
  3627. end)
  3628. it('rejects empty string fillchar', function()
  3629. eq(
  3630. "Invalid 'fillchar': expected single character",
  3631. pcall_err(api.nvim_eval_statusline, '', { fillchar = '' })
  3632. )
  3633. end)
  3634. it('rejects non-string fillchar', function()
  3635. eq(
  3636. "Invalid 'fillchar': expected String, got Integer",
  3637. pcall_err(api.nvim_eval_statusline, '', { fillchar = 1 })
  3638. )
  3639. end)
  3640. it('rejects invalid string', function()
  3641. eq('E539: Illegal character <}>', pcall_err(api.nvim_eval_statusline, '%{%}', {}))
  3642. end)
  3643. it('supports various items', function()
  3644. eq({ str = '0', width = 1 }, api.nvim_eval_statusline('%l', { maxwidth = 5 }))
  3645. command('set readonly')
  3646. eq({ str = '[RO]', width = 4 }, api.nvim_eval_statusline('%r', { maxwidth = 5 }))
  3647. local screen = Screen.new(80, 24)
  3648. command('set showcmd')
  3649. feed('1234')
  3650. screen:expect({ any = '1234' })
  3651. eq({ str = '1234', width = 4 }, api.nvim_eval_statusline('%S', { maxwidth = 5 }))
  3652. feed('56')
  3653. screen:expect({ any = '123456' })
  3654. eq({ str = '<3456', width = 5 }, api.nvim_eval_statusline('%S', { maxwidth = 5 }))
  3655. end)
  3656. describe('highlight parsing', function()
  3657. it('works', function()
  3658. eq(
  3659. {
  3660. str = 'TextWithWarningHighlightTextWithUserHighlight',
  3661. width = 45,
  3662. highlights = {
  3663. { start = 0, group = 'WarningMsg', groups = { 'StatusLine', 'WarningMsg' } },
  3664. { start = 24, group = 'User1', groups = { 'StatusLine', 'User1' } },
  3665. },
  3666. },
  3667. api.nvim_eval_statusline(
  3668. '%#WarningMsg#TextWithWarningHighlight%1*TextWithUserHighlight',
  3669. { highlights = true }
  3670. )
  3671. )
  3672. end)
  3673. it('works with no highlight', function()
  3674. eq({
  3675. str = 'TextWithNoHighlight',
  3676. width = 19,
  3677. highlights = {
  3678. { start = 0, group = 'StatusLine', groups = { 'StatusLine' } },
  3679. },
  3680. }, api.nvim_eval_statusline('TextWithNoHighlight', { highlights = true }))
  3681. end)
  3682. it('works with inactive statusline', function()
  3683. command('split')
  3684. eq(
  3685. {
  3686. str = 'TextWithNoHighlightTextWithWarningHighlight',
  3687. width = 43,
  3688. highlights = {
  3689. { start = 0, group = 'StatusLineNC', groups = { 'StatusLineNC' } },
  3690. { start = 19, group = 'WarningMsg', groups = { 'StatusLineNC', 'WarningMsg' } },
  3691. },
  3692. },
  3693. api.nvim_eval_statusline(
  3694. 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
  3695. { winid = api.nvim_list_wins()[2], highlights = true }
  3696. )
  3697. )
  3698. end)
  3699. it('works with tabline', function()
  3700. eq(
  3701. {
  3702. str = 'TextWithNoHighlightTextWithWarningHighlight',
  3703. width = 43,
  3704. highlights = {
  3705. { start = 0, group = 'TabLineFill', groups = { 'TabLineFill' } },
  3706. { start = 19, group = 'WarningMsg', groups = { 'TabLineFill', 'WarningMsg' } },
  3707. },
  3708. },
  3709. api.nvim_eval_statusline(
  3710. 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
  3711. { use_tabline = true, highlights = true }
  3712. )
  3713. )
  3714. end)
  3715. it('works with winbar', function()
  3716. eq(
  3717. {
  3718. str = 'TextWithNoHighlightTextWithWarningHighlight',
  3719. width = 43,
  3720. highlights = {
  3721. { start = 0, group = 'WinBar', groups = { 'WinBar' } },
  3722. { start = 19, group = 'WarningMsg', groups = { 'WinBar', 'WarningMsg' } },
  3723. },
  3724. },
  3725. api.nvim_eval_statusline(
  3726. 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
  3727. { use_winbar = true, highlights = true }
  3728. )
  3729. )
  3730. end)
  3731. it('works with statuscolumn', function()
  3732. exec([[
  3733. let &stc='%C%s%=%l '
  3734. " should not use "stl" from 'fillchars'
  3735. set cul nu nuw=3 scl=yes:2 fdc=2 fillchars=stl:#
  3736. call setline(1, repeat(['aaaaa'], 5))
  3737. let g:ns = nvim_create_namespace('')
  3738. call sign_define('a', {'text':'aa', 'texthl':'IncSearch', 'numhl':'Normal'})
  3739. call sign_place(2, 1, 'a', bufnr(), {'lnum':4})
  3740. call nvim_buf_set_extmark(0, g:ns, 3, 1, { 'sign_text':'bb', 'sign_hl_group':'ErrorMsg' })
  3741. 1,5fold | 1,5 fold | foldopen!
  3742. norm 4G
  3743. ]])
  3744. eq({
  3745. str = '││bbaa 4 ',
  3746. width = 9,
  3747. highlights = {
  3748. { group = 'CursorLineFold', start = 0, groups = { 'CursorLineFold' } },
  3749. { group = 'Normal', start = 6, groups = { 'Normal' } },
  3750. { group = 'ErrorMsg', start = 6, groups = { 'CursorLineSign', 'ErrorMsg' } },
  3751. { group = 'IncSearch', start = 8, groups = { 'CursorLineSign', 'IncSearch' } },
  3752. { group = 'Normal', start = 10, groups = { 'Normal' } },
  3753. },
  3754. }, api.nvim_eval_statusline(
  3755. '%C%s%=%l ',
  3756. { use_statuscol_lnum = 4, highlights = true }
  3757. ))
  3758. eq(
  3759. {
  3760. str = ' 3 ',
  3761. width = 9,
  3762. highlights = {
  3763. { group = 'LineNr', start = 0, groups = { 'LineNr' } },
  3764. { group = 'ErrorMsg', start = 8, groups = { 'LineNr', 'ErrorMsg' } },
  3765. },
  3766. },
  3767. api.nvim_eval_statusline('%l%#ErrorMsg# ', { use_statuscol_lnum = 3, highlights = true })
  3768. )
  3769. end)
  3770. it('no memory leak with click functions', function()
  3771. api.nvim_eval_statusline('%@ClickFunc@StatusLineStringWithClickFunc%T', {})
  3772. eq({
  3773. str = 'StatusLineStringWithClickFunc',
  3774. width = 29,
  3775. }, api.nvim_eval_statusline('%@ClickFunc@StatusLineStringWithClickFunc%T', {}))
  3776. end)
  3777. end)
  3778. end)
  3779. describe('nvim_parse_cmd', function()
  3780. it('works', function()
  3781. eq({
  3782. cmd = 'echo',
  3783. args = { 'foo' },
  3784. bang = false,
  3785. addr = 'none',
  3786. magic = {
  3787. file = false,
  3788. bar = false,
  3789. },
  3790. nargs = '*',
  3791. nextcmd = '',
  3792. mods = {
  3793. browse = false,
  3794. confirm = false,
  3795. emsg_silent = false,
  3796. filter = {
  3797. pattern = '',
  3798. force = false,
  3799. },
  3800. hide = false,
  3801. horizontal = false,
  3802. keepalt = false,
  3803. keepjumps = false,
  3804. keepmarks = false,
  3805. keeppatterns = false,
  3806. lockmarks = false,
  3807. noautocmd = false,
  3808. noswapfile = false,
  3809. sandbox = false,
  3810. silent = false,
  3811. split = '',
  3812. tab = -1,
  3813. unsilent = false,
  3814. verbose = -1,
  3815. vertical = false,
  3816. },
  3817. }, api.nvim_parse_cmd('echo foo', {}))
  3818. end)
  3819. it('works with ranges', function()
  3820. eq({
  3821. cmd = 'substitute',
  3822. args = { '/math.random/math.max/' },
  3823. bang = false,
  3824. range = { 4, 6 },
  3825. addr = 'line',
  3826. magic = {
  3827. file = false,
  3828. bar = false,
  3829. },
  3830. nargs = '*',
  3831. nextcmd = '',
  3832. mods = {
  3833. browse = false,
  3834. confirm = false,
  3835. emsg_silent = false,
  3836. filter = {
  3837. pattern = '',
  3838. force = false,
  3839. },
  3840. hide = false,
  3841. horizontal = false,
  3842. keepalt = false,
  3843. keepjumps = false,
  3844. keepmarks = false,
  3845. keeppatterns = false,
  3846. lockmarks = false,
  3847. noautocmd = false,
  3848. noswapfile = false,
  3849. sandbox = false,
  3850. silent = false,
  3851. split = '',
  3852. tab = -1,
  3853. unsilent = false,
  3854. verbose = -1,
  3855. vertical = false,
  3856. },
  3857. }, api.nvim_parse_cmd('4,6s/math.random/math.max/', {}))
  3858. end)
  3859. it('works with count', function()
  3860. eq({
  3861. cmd = 'buffer',
  3862. args = {},
  3863. bang = false,
  3864. range = { 1 },
  3865. count = 1,
  3866. addr = 'buf',
  3867. magic = {
  3868. file = false,
  3869. bar = true,
  3870. },
  3871. nargs = '*',
  3872. nextcmd = '',
  3873. mods = {
  3874. browse = false,
  3875. confirm = false,
  3876. emsg_silent = false,
  3877. filter = {
  3878. pattern = '',
  3879. force = false,
  3880. },
  3881. hide = false,
  3882. horizontal = false,
  3883. keepalt = false,
  3884. keepjumps = false,
  3885. keepmarks = false,
  3886. keeppatterns = false,
  3887. lockmarks = false,
  3888. noautocmd = false,
  3889. noswapfile = false,
  3890. sandbox = false,
  3891. silent = false,
  3892. split = '',
  3893. tab = -1,
  3894. unsilent = false,
  3895. verbose = -1,
  3896. vertical = false,
  3897. },
  3898. }, api.nvim_parse_cmd('buffer 1', {}))
  3899. end)
  3900. it('works with register', function()
  3901. eq({
  3902. cmd = 'put',
  3903. args = {},
  3904. bang = false,
  3905. range = {},
  3906. reg = '+',
  3907. addr = 'line',
  3908. magic = {
  3909. file = false,
  3910. bar = true,
  3911. },
  3912. nargs = '0',
  3913. nextcmd = '',
  3914. mods = {
  3915. browse = false,
  3916. confirm = false,
  3917. emsg_silent = false,
  3918. filter = {
  3919. pattern = '',
  3920. force = false,
  3921. },
  3922. hide = false,
  3923. horizontal = false,
  3924. keepalt = false,
  3925. keepjumps = false,
  3926. keepmarks = false,
  3927. keeppatterns = false,
  3928. lockmarks = false,
  3929. noautocmd = false,
  3930. noswapfile = false,
  3931. sandbox = false,
  3932. silent = false,
  3933. split = '',
  3934. tab = -1,
  3935. unsilent = false,
  3936. verbose = -1,
  3937. vertical = false,
  3938. },
  3939. }, api.nvim_parse_cmd('put +', {}))
  3940. eq({
  3941. cmd = 'put',
  3942. args = {},
  3943. bang = false,
  3944. range = {},
  3945. reg = '',
  3946. addr = 'line',
  3947. magic = {
  3948. file = false,
  3949. bar = true,
  3950. },
  3951. nargs = '0',
  3952. nextcmd = '',
  3953. mods = {
  3954. browse = false,
  3955. confirm = false,
  3956. emsg_silent = false,
  3957. filter = {
  3958. pattern = '',
  3959. force = false,
  3960. },
  3961. hide = false,
  3962. horizontal = false,
  3963. keepalt = false,
  3964. keepjumps = false,
  3965. keepmarks = false,
  3966. keeppatterns = false,
  3967. lockmarks = false,
  3968. noautocmd = false,
  3969. noswapfile = false,
  3970. sandbox = false,
  3971. silent = false,
  3972. split = '',
  3973. tab = -1,
  3974. unsilent = false,
  3975. verbose = -1,
  3976. vertical = false,
  3977. },
  3978. }, api.nvim_parse_cmd('put', {}))
  3979. end)
  3980. it('works with range, count and register', function()
  3981. eq({
  3982. cmd = 'delete',
  3983. args = {},
  3984. bang = false,
  3985. range = { 3, 7 },
  3986. count = 7,
  3987. reg = '*',
  3988. addr = 'line',
  3989. magic = {
  3990. file = false,
  3991. bar = true,
  3992. },
  3993. nargs = '0',
  3994. nextcmd = '',
  3995. mods = {
  3996. browse = false,
  3997. confirm = false,
  3998. emsg_silent = false,
  3999. filter = {
  4000. pattern = '',
  4001. force = false,
  4002. },
  4003. hide = false,
  4004. horizontal = false,
  4005. keepalt = false,
  4006. keepjumps = false,
  4007. keepmarks = false,
  4008. keeppatterns = false,
  4009. lockmarks = false,
  4010. noautocmd = false,
  4011. noswapfile = false,
  4012. sandbox = false,
  4013. silent = false,
  4014. split = '',
  4015. tab = -1,
  4016. unsilent = false,
  4017. verbose = -1,
  4018. vertical = false,
  4019. },
  4020. }, api.nvim_parse_cmd('1,3delete * 5', {}))
  4021. end)
  4022. it('works with bang', function()
  4023. eq({
  4024. cmd = 'write',
  4025. args = {},
  4026. bang = true,
  4027. range = {},
  4028. addr = 'line',
  4029. magic = {
  4030. file = true,
  4031. bar = true,
  4032. },
  4033. nargs = '?',
  4034. nextcmd = '',
  4035. mods = {
  4036. browse = false,
  4037. confirm = false,
  4038. emsg_silent = false,
  4039. filter = {
  4040. pattern = '',
  4041. force = false,
  4042. },
  4043. hide = false,
  4044. horizontal = false,
  4045. keepalt = false,
  4046. keepjumps = false,
  4047. keepmarks = false,
  4048. keeppatterns = false,
  4049. lockmarks = false,
  4050. noautocmd = false,
  4051. noswapfile = false,
  4052. sandbox = false,
  4053. silent = false,
  4054. split = '',
  4055. tab = -1,
  4056. unsilent = false,
  4057. verbose = -1,
  4058. vertical = false,
  4059. },
  4060. }, api.nvim_parse_cmd('w!', {}))
  4061. end)
  4062. it('works with modifiers', function()
  4063. eq(
  4064. {
  4065. cmd = 'split',
  4066. args = { 'foo.txt' },
  4067. bang = false,
  4068. range = {},
  4069. addr = '?',
  4070. magic = {
  4071. file = true,
  4072. bar = true,
  4073. },
  4074. nargs = '?',
  4075. nextcmd = '',
  4076. mods = {
  4077. browse = false,
  4078. confirm = false,
  4079. emsg_silent = true,
  4080. filter = {
  4081. pattern = 'foo',
  4082. force = false,
  4083. },
  4084. hide = false,
  4085. horizontal = true,
  4086. keepalt = false,
  4087. keepjumps = false,
  4088. keepmarks = false,
  4089. keeppatterns = false,
  4090. lockmarks = false,
  4091. noautocmd = false,
  4092. noswapfile = false,
  4093. sandbox = false,
  4094. silent = true,
  4095. split = 'topleft',
  4096. tab = 1,
  4097. unsilent = false,
  4098. verbose = 15,
  4099. vertical = false,
  4100. },
  4101. },
  4102. api.nvim_parse_cmd(
  4103. '15verbose silent! horizontal topleft tab filter /foo/ split foo.txt',
  4104. {}
  4105. )
  4106. )
  4107. eq(
  4108. {
  4109. cmd = 'split',
  4110. args = { 'foo.txt' },
  4111. bang = false,
  4112. range = {},
  4113. addr = '?',
  4114. magic = {
  4115. file = true,
  4116. bar = true,
  4117. },
  4118. nargs = '?',
  4119. nextcmd = '',
  4120. mods = {
  4121. browse = false,
  4122. confirm = true,
  4123. emsg_silent = false,
  4124. filter = {
  4125. pattern = 'foo',
  4126. force = true,
  4127. },
  4128. hide = false,
  4129. horizontal = false,
  4130. keepalt = false,
  4131. keepjumps = false,
  4132. keepmarks = false,
  4133. keeppatterns = false,
  4134. lockmarks = false,
  4135. noautocmd = false,
  4136. noswapfile = false,
  4137. sandbox = false,
  4138. silent = false,
  4139. split = 'botright',
  4140. tab = 0,
  4141. unsilent = true,
  4142. verbose = 0,
  4143. vertical = false,
  4144. },
  4145. },
  4146. api.nvim_parse_cmd(
  4147. '0verbose unsilent botright 0tab confirm filter! /foo/ split foo.txt',
  4148. {}
  4149. )
  4150. )
  4151. end)
  4152. it('works with user commands', function()
  4153. command('command -bang -nargs=+ -range -addr=lines MyCommand echo foo')
  4154. eq({
  4155. cmd = 'MyCommand',
  4156. args = { 'test', 'it' },
  4157. bang = true,
  4158. range = { 4, 6 },
  4159. addr = 'line',
  4160. magic = {
  4161. file = false,
  4162. bar = false,
  4163. },
  4164. nargs = '+',
  4165. nextcmd = '',
  4166. mods = {
  4167. browse = false,
  4168. confirm = false,
  4169. emsg_silent = false,
  4170. filter = {
  4171. pattern = '',
  4172. force = false,
  4173. },
  4174. hide = false,
  4175. horizontal = false,
  4176. keepalt = false,
  4177. keepjumps = false,
  4178. keepmarks = false,
  4179. keeppatterns = false,
  4180. lockmarks = false,
  4181. noautocmd = false,
  4182. noswapfile = false,
  4183. sandbox = false,
  4184. silent = false,
  4185. split = '',
  4186. tab = -1,
  4187. unsilent = false,
  4188. verbose = -1,
  4189. vertical = false,
  4190. },
  4191. }, api.nvim_parse_cmd('4,6MyCommand! test it', {}))
  4192. end)
  4193. it('works for commands separated by bar', function()
  4194. eq({
  4195. cmd = 'argadd',
  4196. args = { 'a.txt' },
  4197. bang = false,
  4198. range = {},
  4199. addr = 'arg',
  4200. magic = {
  4201. file = true,
  4202. bar = true,
  4203. },
  4204. nargs = '*',
  4205. nextcmd = 'argadd b.txt',
  4206. mods = {
  4207. browse = false,
  4208. confirm = false,
  4209. emsg_silent = false,
  4210. filter = {
  4211. pattern = '',
  4212. force = false,
  4213. },
  4214. hide = false,
  4215. horizontal = false,
  4216. keepalt = false,
  4217. keepjumps = false,
  4218. keepmarks = false,
  4219. keeppatterns = false,
  4220. lockmarks = false,
  4221. noautocmd = false,
  4222. noswapfile = false,
  4223. sandbox = false,
  4224. silent = false,
  4225. split = '',
  4226. tab = -1,
  4227. unsilent = false,
  4228. verbose = -1,
  4229. vertical = false,
  4230. },
  4231. }, api.nvim_parse_cmd('argadd a.txt | argadd b.txt', {}))
  4232. end)
  4233. it('works for nargs=1', function()
  4234. command('command -nargs=1 MyCommand echo <q-args>')
  4235. eq({
  4236. cmd = 'MyCommand',
  4237. args = { 'test it' },
  4238. bang = false,
  4239. addr = 'none',
  4240. magic = {
  4241. file = false,
  4242. bar = false,
  4243. },
  4244. nargs = '1',
  4245. nextcmd = '',
  4246. mods = {
  4247. browse = false,
  4248. confirm = false,
  4249. emsg_silent = false,
  4250. filter = {
  4251. pattern = '',
  4252. force = false,
  4253. },
  4254. hide = false,
  4255. horizontal = false,
  4256. keepalt = false,
  4257. keepjumps = false,
  4258. keepmarks = false,
  4259. keeppatterns = false,
  4260. lockmarks = false,
  4261. noautocmd = false,
  4262. noswapfile = false,
  4263. sandbox = false,
  4264. silent = false,
  4265. split = '',
  4266. tab = -1,
  4267. unsilent = false,
  4268. verbose = -1,
  4269. vertical = false,
  4270. },
  4271. }, api.nvim_parse_cmd('MyCommand test it', {}))
  4272. end)
  4273. it('validates command', function()
  4274. eq('Error while parsing command line', pcall_err(api.nvim_parse_cmd, '', {}))
  4275. eq('Error while parsing command line', pcall_err(api.nvim_parse_cmd, '" foo', {}))
  4276. eq(
  4277. 'Error while parsing command line: E492: Not an editor command: Fubar',
  4278. pcall_err(api.nvim_parse_cmd, 'Fubar', {})
  4279. )
  4280. command('command! Fubar echo foo')
  4281. eq(
  4282. 'Error while parsing command line: E477: No ! allowed',
  4283. pcall_err(api.nvim_parse_cmd, 'Fubar!', {})
  4284. )
  4285. eq(
  4286. 'Error while parsing command line: E481: No range allowed',
  4287. pcall_err(api.nvim_parse_cmd, '4,6Fubar', {})
  4288. )
  4289. command('command! Foobar echo foo')
  4290. eq(
  4291. 'Error while parsing command line: E464: Ambiguous use of user-defined command',
  4292. pcall_err(api.nvim_parse_cmd, 'F', {})
  4293. )
  4294. end)
  4295. it('does not interfere with printing line in Ex mode #19400', function()
  4296. local screen = Screen.new(60, 7)
  4297. insert([[
  4298. foo
  4299. bar]])
  4300. feed('gQ1')
  4301. screen:expect([[
  4302. foo |
  4303. bar |
  4304. {1:~ }|*2
  4305. {3: }|
  4306. Entering Ex mode. Type "visual" to go to Normal mode. |
  4307. :1^ |
  4308. ]])
  4309. eq('Error while parsing command line', pcall_err(api.nvim_parse_cmd, '', {}))
  4310. feed('<CR>')
  4311. screen:expect([[
  4312. foo |
  4313. bar |
  4314. {3: }|
  4315. Entering Ex mode. Type "visual" to go to Normal mode. |
  4316. :1 |
  4317. foo |
  4318. :^ |
  4319. ]])
  4320. end)
  4321. it('does not move cursor or change search history/pattern #19878 #19890', function()
  4322. api.nvim_buf_set_lines(0, 0, -1, true, { 'foo', 'bar', 'foo', 'bar' })
  4323. eq({ 1, 0 }, api.nvim_win_get_cursor(0))
  4324. eq('', fn.getreg('/'))
  4325. eq('', fn.histget('search'))
  4326. feed(':') -- call the API in cmdline mode to test whether it changes search history
  4327. eq({
  4328. cmd = 'normal',
  4329. args = { 'x' },
  4330. bang = true,
  4331. range = { 3, 4 },
  4332. addr = 'line',
  4333. magic = {
  4334. file = false,
  4335. bar = false,
  4336. },
  4337. nargs = '+',
  4338. nextcmd = '',
  4339. mods = {
  4340. browse = false,
  4341. confirm = false,
  4342. emsg_silent = false,
  4343. filter = {
  4344. pattern = '',
  4345. force = false,
  4346. },
  4347. hide = false,
  4348. horizontal = false,
  4349. keepalt = false,
  4350. keepjumps = false,
  4351. keepmarks = false,
  4352. keeppatterns = false,
  4353. lockmarks = false,
  4354. noautocmd = false,
  4355. noswapfile = false,
  4356. sandbox = false,
  4357. silent = false,
  4358. split = '',
  4359. tab = -1,
  4360. unsilent = false,
  4361. verbose = -1,
  4362. vertical = false,
  4363. },
  4364. }, api.nvim_parse_cmd('+2;/bar/normal! x', {}))
  4365. eq({ 1, 0 }, api.nvim_win_get_cursor(0))
  4366. eq('', fn.getreg('/'))
  4367. eq('', fn.histget('search'))
  4368. end)
  4369. it('result can be used directly by nvim_cmd #20051', function()
  4370. eq('foo', api.nvim_cmd(api.nvim_parse_cmd('echo "foo"', {}), { output = true }))
  4371. api.nvim_cmd(api.nvim_parse_cmd('set cursorline', {}), {})
  4372. eq(true, api.nvim_get_option_value('cursorline', {}))
  4373. end)
  4374. it('no side-effects (error messages) in pcall() #20339', function()
  4375. eq(
  4376. { false, 'Error while parsing command line: E16: Invalid range' },
  4377. exec_lua([=[return {pcall(vim.api.nvim_parse_cmd, "'<,'>n", {})}]=])
  4378. )
  4379. eq('', eval('v:errmsg'))
  4380. end)
  4381. end)
  4382. describe('nvim_cmd', function()
  4383. it('works', function()
  4384. api.nvim_cmd({ cmd = 'set', args = { 'cursorline' } }, {})
  4385. eq(true, api.nvim_get_option_value('cursorline', {}))
  4386. end)
  4387. it('validation', function()
  4388. eq("Invalid 'cmd': expected non-empty String", pcall_err(api.nvim_cmd, { cmd = '' }, {}))
  4389. eq("Invalid 'cmd': expected String, got Array", pcall_err(api.nvim_cmd, { cmd = {} }, {}))
  4390. eq(
  4391. "Invalid 'args': expected Array, got Boolean",
  4392. pcall_err(api.nvim_cmd, { cmd = 'set', args = true }, {})
  4393. )
  4394. eq(
  4395. 'Invalid command arg: expected non-whitespace',
  4396. pcall_err(api.nvim_cmd, { cmd = 'set', args = { ' ' } }, {})
  4397. )
  4398. eq(
  4399. 'Invalid command arg: expected valid type, got Array',
  4400. pcall_err(api.nvim_cmd, { cmd = 'set', args = { {} } }, {})
  4401. )
  4402. eq('Wrong number of arguments', pcall_err(api.nvim_cmd, { cmd = 'aboveleft', args = {} }, {}))
  4403. eq(
  4404. 'Command cannot accept bang: print',
  4405. pcall_err(api.nvim_cmd, { cmd = 'print', args = {}, bang = true }, {})
  4406. )
  4407. eq(
  4408. 'Command cannot accept range: set',
  4409. pcall_err(api.nvim_cmd, { cmd = 'set', args = {}, range = { 1 } }, {})
  4410. )
  4411. eq(
  4412. "Invalid 'range': expected Array, got Boolean",
  4413. pcall_err(api.nvim_cmd, { cmd = 'print', args = {}, range = true }, {})
  4414. )
  4415. eq(
  4416. "Invalid 'range': expected <=2 elements",
  4417. pcall_err(api.nvim_cmd, { cmd = 'print', args = {}, range = { 1, 2, 3, 4 } }, {})
  4418. )
  4419. eq(
  4420. 'Invalid range element: expected non-negative Integer',
  4421. pcall_err(api.nvim_cmd, { cmd = 'print', args = {}, range = { -1 } }, {})
  4422. )
  4423. eq(
  4424. 'Command cannot accept count: set',
  4425. pcall_err(api.nvim_cmd, { cmd = 'set', args = {}, count = 1 }, {})
  4426. )
  4427. eq(
  4428. "Invalid 'count': expected Integer, got Boolean",
  4429. pcall_err(api.nvim_cmd, { cmd = 'print', args = {}, count = true }, {})
  4430. )
  4431. eq(
  4432. "Invalid 'count': expected non-negative Integer",
  4433. pcall_err(api.nvim_cmd, { cmd = 'print', args = {}, count = -1 }, {})
  4434. )
  4435. eq(
  4436. 'Command cannot accept register: set',
  4437. pcall_err(api.nvim_cmd, { cmd = 'set', args = {}, reg = 'x' }, {})
  4438. )
  4439. eq(
  4440. 'Cannot use register "=',
  4441. pcall_err(api.nvim_cmd, { cmd = 'put', args = {}, reg = '=' }, {})
  4442. )
  4443. eq(
  4444. "Invalid 'reg': expected single character, got xx",
  4445. pcall_err(api.nvim_cmd, { cmd = 'put', args = {}, reg = 'xx' }, {})
  4446. )
  4447. -- #20681
  4448. eq('Invalid command: "win_getid"', pcall_err(api.nvim_cmd, { cmd = 'win_getid' }, {}))
  4449. eq('Invalid command: "echo "hi""', pcall_err(api.nvim_cmd, { cmd = 'echo "hi"' }, {}))
  4450. eq('Invalid command: "win_getid"', pcall_err(exec_lua, [[return vim.cmd.win_getid{}]]))
  4451. -- Lua call allows empty {} for dict item.
  4452. eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, magic = {} }]]))
  4453. eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, mods = {} }]]))
  4454. eq('', api.nvim_cmd({ cmd = 'set', args = {}, magic = {} }, {}))
  4455. -- Lua call does not allow non-empty list-like {} for dict item.
  4456. eq(
  4457. "Invalid 'magic': Expected Dict-like Lua table",
  4458. pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { 'a' } }]])
  4459. )
  4460. eq(
  4461. "Invalid key: 'bogus'",
  4462. pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { bogus = true } }]])
  4463. )
  4464. eq(
  4465. "Invalid key: 'bogus'",
  4466. pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, mods = { bogus = true } }]])
  4467. )
  4468. end)
  4469. it('captures output', function()
  4470. eq('foo', api.nvim_cmd({ cmd = 'echo', args = { '"foo"' } }, { output = true }))
  4471. end)
  4472. it('sets correct script context', function()
  4473. api.nvim_cmd({ cmd = 'set', args = { 'cursorline' } }, {})
  4474. local str = exec_capture([[verbose set cursorline?]])
  4475. neq(nil, str:find('cursorline\n\tLast set from API client %(channel id %d+%)'))
  4476. end)
  4477. it('works with range', function()
  4478. insert [[
  4479. line1
  4480. line2
  4481. line3
  4482. line4
  4483. you didn't expect this
  4484. line5
  4485. line6
  4486. ]]
  4487. api.nvim_cmd({ cmd = 'del', range = { 2, 4 } }, {})
  4488. expect [[
  4489. line1
  4490. you didn't expect this
  4491. line5
  4492. line6
  4493. ]]
  4494. end)
  4495. it('works with count', function()
  4496. insert [[
  4497. line1
  4498. line2
  4499. line3
  4500. line4
  4501. you didn't expect this
  4502. line5
  4503. line6
  4504. ]]
  4505. api.nvim_cmd({ cmd = 'del', range = { 2 }, count = 4 }, {})
  4506. expect [[
  4507. line1
  4508. line5
  4509. line6
  4510. ]]
  4511. end)
  4512. it('works with register', function()
  4513. insert [[
  4514. line1
  4515. line2
  4516. line3
  4517. line4
  4518. you didn't expect this
  4519. line5
  4520. line6
  4521. ]]
  4522. api.nvim_cmd({ cmd = 'del', range = { 2, 4 }, reg = 'a' }, {})
  4523. command('1put a')
  4524. expect [[
  4525. line1
  4526. line2
  4527. line3
  4528. line4
  4529. you didn't expect this
  4530. line5
  4531. line6
  4532. ]]
  4533. end)
  4534. it('works with bang', function()
  4535. api.nvim_create_user_command('Foo', 'echo "<bang>"', { bang = true })
  4536. eq('!', api.nvim_cmd({ cmd = 'Foo', bang = true }, { output = true }))
  4537. eq('', api.nvim_cmd({ cmd = 'Foo', bang = false }, { output = true }))
  4538. end)
  4539. it('works with modifiers', function()
  4540. -- with silent = true output is still captured
  4541. eq(
  4542. '1',
  4543. api.nvim_cmd(
  4544. { cmd = 'echomsg', args = { '1' }, mods = { silent = true } },
  4545. { output = true }
  4546. )
  4547. )
  4548. -- but message isn't added to message history
  4549. eq('', api.nvim_cmd({ cmd = 'messages' }, { output = true }))
  4550. api.nvim_create_user_command('Foo', 'set verbose', {})
  4551. eq(' verbose=1', api.nvim_cmd({ cmd = 'Foo', mods = { verbose = 1 } }, { output = true }))
  4552. api.nvim_create_user_command('Mods', "echo '<mods>'", {})
  4553. eq(
  4554. 'keepmarks keeppatterns silent 3verbose aboveleft horizontal',
  4555. api.nvim_cmd({
  4556. cmd = 'Mods',
  4557. mods = {
  4558. horizontal = true,
  4559. keepmarks = true,
  4560. keeppatterns = true,
  4561. silent = true,
  4562. split = 'aboveleft',
  4563. verbose = 3,
  4564. },
  4565. }, { output = true })
  4566. )
  4567. eq(0, api.nvim_get_option_value('verbose', {}))
  4568. command('edit foo.txt | edit bar.txt')
  4569. eq(
  4570. ' 1 #h "foo.txt" line 1',
  4571. api.nvim_cmd(
  4572. { cmd = 'buffers', mods = { filter = { pattern = 'foo', force = false } } },
  4573. { output = true }
  4574. )
  4575. )
  4576. eq(
  4577. ' 2 %a "bar.txt" line 1',
  4578. api.nvim_cmd(
  4579. { cmd = 'buffers', mods = { filter = { pattern = 'foo', force = true } } },
  4580. { output = true }
  4581. )
  4582. )
  4583. -- with emsg_silent = true error is suppressed
  4584. feed([[:lua vim.api.nvim_cmd({ cmd = 'call', mods = { emsg_silent = true } }, {})<CR>]])
  4585. eq('', api.nvim_cmd({ cmd = 'messages' }, { output = true }))
  4586. -- error from the next command typed is not suppressed #21420
  4587. feed(':call<CR><CR>')
  4588. eq('E471: Argument required', api.nvim_cmd({ cmd = 'messages' }, { output = true }))
  4589. end)
  4590. it('works with magic.file', function()
  4591. exec_lua([[
  4592. vim.api.nvim_create_user_command("Foo", function(opts)
  4593. vim.api.nvim_echo({{ opts.fargs[1] }}, false, {})
  4594. end, { nargs = 1 })
  4595. ]])
  4596. eq(
  4597. uv.cwd(),
  4598. api.nvim_cmd(
  4599. { cmd = 'Foo', args = { '%:p:h' }, magic = { file = true } },
  4600. { output = true }
  4601. )
  4602. )
  4603. end)
  4604. it('splits arguments correctly', function()
  4605. exec([[
  4606. function! FooFunc(...)
  4607. echo a:000
  4608. endfunction
  4609. ]])
  4610. api.nvim_create_user_command('Foo', 'call FooFunc(<f-args>)', { nargs = '+' })
  4611. eq(
  4612. [=[['a quick', 'brown fox', 'jumps over the', 'lazy dog']]=],
  4613. api.nvim_cmd(
  4614. { cmd = 'Foo', args = { 'a quick', 'brown fox', 'jumps over the', 'lazy dog' } },
  4615. { output = true }
  4616. )
  4617. )
  4618. eq(
  4619. [=[['test \ \\ \"""\', 'more\ tests\" ']]=],
  4620. api.nvim_cmd(
  4621. { cmd = 'Foo', args = { [[test \ \\ \"""\]], [[more\ tests\" ]] } },
  4622. { output = true }
  4623. )
  4624. )
  4625. end)
  4626. it('splits arguments correctly for Lua callback', function()
  4627. api.nvim_exec_lua(
  4628. [[
  4629. local function FooFunc(opts)
  4630. vim.print(opts.fargs)
  4631. end
  4632. vim.api.nvim_create_user_command("Foo", FooFunc, { nargs = '+' })
  4633. ]],
  4634. {}
  4635. )
  4636. eq(
  4637. [[{ "a quick", "brown fox", "jumps over the", "lazy dog" }]],
  4638. api.nvim_cmd(
  4639. { cmd = 'Foo', args = { 'a quick', 'brown fox', 'jumps over the', 'lazy dog' } },
  4640. { output = true }
  4641. )
  4642. )
  4643. eq(
  4644. [[{ 'test \\ \\\\ \\"""\\', 'more\\ tests\\" ' }]],
  4645. api.nvim_cmd(
  4646. { cmd = 'Foo', args = { [[test \ \\ \"""\]], [[more\ tests\" ]] } },
  4647. { output = true }
  4648. )
  4649. )
  4650. end)
  4651. it('works with buffer names', function()
  4652. command('edit foo.txt | edit bar.txt')
  4653. api.nvim_cmd({ cmd = 'buffer', args = { 'foo.txt' } }, {})
  4654. eq('foo.txt', fn.fnamemodify(api.nvim_buf_get_name(0), ':t'))
  4655. api.nvim_cmd({ cmd = 'buffer', args = { 'bar.txt' } }, {})
  4656. eq('bar.txt', fn.fnamemodify(api.nvim_buf_get_name(0), ':t'))
  4657. end)
  4658. it('triggers CmdUndefined event if command is not found', function()
  4659. api.nvim_exec_lua(
  4660. [[
  4661. vim.api.nvim_create_autocmd("CmdUndefined",
  4662. { pattern = "Foo",
  4663. callback = function()
  4664. vim.api.nvim_create_user_command("Foo", "echo 'foo'", {})
  4665. end
  4666. })
  4667. ]],
  4668. {}
  4669. )
  4670. eq('foo', api.nvim_cmd({ cmd = 'Foo' }, { output = true }))
  4671. end)
  4672. it('errors if command is not implemented', function()
  4673. eq('Command not implemented: winpos', pcall_err(api.nvim_cmd, { cmd = 'winpos' }, {}))
  4674. end)
  4675. it('works with empty arguments list', function()
  4676. api.nvim_cmd({ cmd = 'update' }, {})
  4677. api.nvim_cmd({ cmd = 'buffer', count = 0 }, {})
  4678. end)
  4679. it("doesn't suppress errors when used in keymapping", function()
  4680. api.nvim_exec_lua(
  4681. [[
  4682. vim.keymap.set("n", "[l",
  4683. function() vim.api.nvim_cmd({ cmd = "echo", args = {"foo"} }, {}) end)
  4684. ]],
  4685. {}
  4686. )
  4687. feed('[l')
  4688. neq(nil, string.find(eval('v:errmsg'), 'E5108:'))
  4689. end)
  4690. it('handles 0 range #19608', function()
  4691. api.nvim_buf_set_lines(0, 0, -1, false, { 'aa' })
  4692. api.nvim_cmd({ cmd = 'delete', range = { 0 } }, {})
  4693. command('undo')
  4694. eq({ 'aa' }, api.nvim_buf_get_lines(0, 0, 1, false))
  4695. assert_alive()
  4696. end)
  4697. it('supports filename expansion', function()
  4698. api.nvim_cmd({ cmd = 'argadd', args = { '%:p:h:t', '%:p:h:t' } }, {})
  4699. local arg = fn.expand('%:p:h:t')
  4700. eq({ arg, arg }, fn.argv())
  4701. end)
  4702. it(":make command works when argument count isn't 1 #19696", function()
  4703. command('set makeprg=echo')
  4704. command('set shellquote=')
  4705. matches('^:!echo ', api.nvim_cmd({ cmd = 'make' }, { output = true }))
  4706. assert_alive()
  4707. matches(
  4708. '^:!echo foo bar',
  4709. api.nvim_cmd({ cmd = 'make', args = { 'foo', 'bar' } }, { output = true })
  4710. )
  4711. assert_alive()
  4712. local arg_pesc = pesc(fn.expand('%:p:h:t'))
  4713. matches(
  4714. ('^:!echo %s %s'):format(arg_pesc, arg_pesc),
  4715. api.nvim_cmd({ cmd = 'make', args = { '%:p:h:t', '%:p:h:t' } }, { output = true })
  4716. )
  4717. assert_alive()
  4718. end)
  4719. it("doesn't display messages when output=true", function()
  4720. local screen = Screen.new(40, 6)
  4721. api.nvim_cmd({ cmd = 'echo', args = { [['hello']] } }, { output = true })
  4722. screen:expect {
  4723. grid = [[
  4724. ^ |
  4725. {1:~ }|*4
  4726. |
  4727. ]],
  4728. }
  4729. exec([[
  4730. func Print()
  4731. call nvim_cmd(#{cmd: 'echo', args: ['"hello"']}, #{output: v:true})
  4732. endfunc
  4733. ]])
  4734. feed([[:echon 1 | call Print() | echon 5<CR>]])
  4735. screen:expect {
  4736. grid = [[
  4737. ^ |
  4738. {1:~ }|*4
  4739. 15 |
  4740. ]],
  4741. }
  4742. end)
  4743. it('works with non-String args', function()
  4744. eq('2', api.nvim_cmd({ cmd = 'echo', args = { 2 } }, { output = true }))
  4745. eq('1', api.nvim_cmd({ cmd = 'echo', args = { true } }, { output = true }))
  4746. end)
  4747. describe('first argument as count', function()
  4748. it('works', function()
  4749. command('vsplit | enew')
  4750. api.nvim_cmd({ cmd = 'bdelete', args = { api.nvim_get_current_buf() } }, {})
  4751. eq(1, api.nvim_get_current_buf())
  4752. end)
  4753. it('works with :sleep using milliseconds', function()
  4754. local start = uv.now()
  4755. api.nvim_cmd({ cmd = 'sleep', args = { '100m' } }, {})
  4756. ok(uv.now() - start <= 300)
  4757. end)
  4758. end)
  4759. it(':call with unknown function does not crash #26289', function()
  4760. eq(
  4761. 'Vim:E117: Unknown function: UnknownFunc',
  4762. pcall_err(api.nvim_cmd, { cmd = 'call', args = { 'UnknownFunc()' } }, {})
  4763. )
  4764. end)
  4765. it(':throw does not crash #24556', function()
  4766. eq('42', pcall_err(api.nvim_cmd, { cmd = 'throw', args = { '42' } }, {}))
  4767. end)
  4768. it('can use :return #24556', function()
  4769. exec([[
  4770. func Foo()
  4771. let g:pos = 'before'
  4772. call nvim_cmd({'cmd': 'return', 'args': ['[1, 2, 3]']}, {})
  4773. let g:pos = 'after'
  4774. endfunc
  4775. let g:result = Foo()
  4776. ]])
  4777. eq('before', api.nvim_get_var('pos'))
  4778. eq({ 1, 2, 3 }, api.nvim_get_var('result'))
  4779. end)
  4780. it('errors properly when command too recursive #27210', function()
  4781. exec_lua([[
  4782. _G.success = false
  4783. vim.api.nvim_create_user_command('Test', function()
  4784. vim.api.nvim_cmd({ cmd = 'Test' }, {})
  4785. _G.success = true
  4786. end, {})
  4787. ]])
  4788. pcall_err(command, 'Test')
  4789. assert_alive()
  4790. eq(false, exec_lua('return _G.success'))
  4791. end)
  4792. end)
  4793. it('nvim__redraw', function()
  4794. local screen = Screen.new(60, 5)
  4795. eq('at least one action required', pcall_err(api.nvim__redraw, {}))
  4796. eq('at least one action required', pcall_err(api.nvim__redraw, { buf = 0 }))
  4797. eq('at least one action required', pcall_err(api.nvim__redraw, { win = 0 }))
  4798. eq("cannot use both 'buf' and 'win'", pcall_err(api.nvim__redraw, { buf = 0, win = 0 }))
  4799. local win = api.nvim_get_current_win()
  4800. -- Can move cursor to recently opened window and window is flushed #28868
  4801. feed(':echo getchar()<CR>')
  4802. local newwin = api.nvim_open_win(0, false, {
  4803. relative = 'editor',
  4804. width = 1,
  4805. height = 1,
  4806. row = 1,
  4807. col = 10,
  4808. })
  4809. api.nvim__redraw({ win = newwin, cursor = true })
  4810. screen:expect({
  4811. grid = [[
  4812. |
  4813. {1:~ }{4:^ }{1: }|
  4814. {1:~ }|*2
  4815. :echo getchar() |
  4816. ]],
  4817. })
  4818. fn.setline(1, 'foobar')
  4819. command('vnew')
  4820. fn.setline(1, 'foobaz')
  4821. -- Can flush pending screen updates
  4822. api.nvim__redraw({ flush = true })
  4823. screen:expect({
  4824. grid = [[
  4825. foobaz │foobar |
  4826. {1:~ }{4:^f}{1: }│{1:~ }|
  4827. {1:~ }│{1:~ }|
  4828. {3:[No Name] [+] }{2:[No Name] [+] }|
  4829. :echo getchar() |
  4830. ]],
  4831. })
  4832. api.nvim_win_close(newwin, true)
  4833. -- Can update the grid cursor position #20793
  4834. api.nvim__redraw({ cursor = true })
  4835. screen:expect({
  4836. grid = [[
  4837. ^foobaz │foobar |
  4838. {1:~ }│{1:~ }|*2
  4839. {3:[No Name] [+] }{2:[No Name] [+] }|
  4840. :echo getchar() |
  4841. ]],
  4842. })
  4843. -- Also in non-current window
  4844. api.nvim__redraw({ cursor = true, win = win })
  4845. screen:expect({
  4846. grid = [[
  4847. foobaz │^foobar |
  4848. {1:~ }│{1:~ }|*2
  4849. {3:[No Name] [+] }{2:[No Name] [+] }|
  4850. :echo getchar() |
  4851. ]],
  4852. })
  4853. -- Can update the 'statusline' in a single window
  4854. api.nvim_set_option_value('statusline', 'statusline1', { win = 0 })
  4855. api.nvim_set_option_value('statusline', 'statusline2', { win = win })
  4856. api.nvim__redraw({ cursor = true, win = 0, statusline = true })
  4857. screen:expect({
  4858. grid = [[
  4859. ^foobaz │foobar |
  4860. {1:~ }│{1:~ }|*2
  4861. {3:statusline1 }{2:[No Name] [+] }|
  4862. :echo getchar() |
  4863. ]],
  4864. })
  4865. api.nvim__redraw({ win = win, statusline = true })
  4866. screen:expect({
  4867. grid = [[
  4868. ^foobaz │foobar |
  4869. {1:~ }│{1:~ }|*2
  4870. {3:statusline1 }{2:statusline2 }|
  4871. :echo getchar() |
  4872. ]],
  4873. })
  4874. -- Can update the 'statusline' in all windows
  4875. api.nvim_set_option_value('statusline', '', { win = win })
  4876. api.nvim_set_option_value('statusline', 'statusline3', {})
  4877. api.nvim__redraw({ statusline = true })
  4878. screen:expect({
  4879. grid = [[
  4880. ^foobaz │foobar |
  4881. {1:~ }│{1:~ }|*2
  4882. {3:statusline3 }{2:statusline3 }|
  4883. :echo getchar() |
  4884. ]],
  4885. })
  4886. -- Can update the 'statuscolumn'
  4887. api.nvim_set_option_value('statuscolumn', 'statuscolumn', { win = win })
  4888. api.nvim__redraw({ statuscolumn = true })
  4889. screen:expect({
  4890. grid = [[
  4891. ^foobaz │{8:statuscolumn}foobar |
  4892. {1:~ }│{1:~ }|*2
  4893. {3:statusline3 }{2:statusline3 }|
  4894. :echo getchar() |
  4895. ]],
  4896. })
  4897. -- Can update the 'winbar'
  4898. api.nvim_set_option_value('winbar', 'winbar', { win = 0 })
  4899. api.nvim__redraw({ win = 0, winbar = true })
  4900. screen:expect({
  4901. grid = [[
  4902. {5:^winbar }│{8:statuscolumn}foobar |
  4903. foobaz │{1:~ }|
  4904. {1:~ }│{1:~ }|
  4905. {3:statusline3 }{2:statusline3 }|
  4906. :echo getchar() |
  4907. ]],
  4908. })
  4909. -- Can update the 'tabline'
  4910. api.nvim_set_option_value('showtabline', 2, {})
  4911. api.nvim_set_option_value('tabline', 'tabline', {})
  4912. api.nvim__redraw({ tabline = true })
  4913. screen:expect({
  4914. grid = [[
  4915. {2:^tabline }|
  4916. {5:winbar }│{8:statuscolumn}foobar |
  4917. foobaz │{1:~ }|
  4918. {3:statusline3 }{2:statusline3 }|
  4919. :echo getchar() |
  4920. ]],
  4921. })
  4922. -- Can update multiple status widgets
  4923. api.nvim_set_option_value('tabline', 'tabline2', {})
  4924. api.nvim_set_option_value('statusline', 'statusline4', {})
  4925. api.nvim__redraw({ statusline = true, tabline = true })
  4926. screen:expect({
  4927. grid = [[
  4928. {2:^tabline2 }|
  4929. {5:winbar }│{8:statuscolumn}foobar |
  4930. foobaz │{1:~ }|
  4931. {3:statusline4 }{2:statusline4 }|
  4932. :echo getchar() |
  4933. ]],
  4934. })
  4935. -- Can update all status widgets
  4936. api.nvim_set_option_value('tabline', 'tabline3', {})
  4937. api.nvim_set_option_value('statusline', 'statusline5', {})
  4938. api.nvim_set_option_value('statuscolumn', 'statuscolumn2', {})
  4939. api.nvim_set_option_value('winbar', 'winbar2', {})
  4940. api.nvim__redraw({ statuscolumn = true, statusline = true, tabline = true, winbar = true })
  4941. screen:expect({
  4942. grid = [[
  4943. {2:^tabline3 }|
  4944. {5:winbar2 }│{5:winbar2 }|
  4945. {8:statuscolumn2}foobaz │{8:statuscolumn}foobar |
  4946. {3:statusline5 }{2:statusline5 }|
  4947. :echo getchar() |
  4948. ]],
  4949. })
  4950. -- Can update status widget for a specific window
  4951. feed('<CR><CR>')
  4952. command('let g:status=0')
  4953. api.nvim_set_option_value('statusline', '%{%g:status%}', { win = 0 })
  4954. command('vsplit')
  4955. screen:expect({
  4956. grid = [[
  4957. {2:tabline3 }|
  4958. {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
  4959. {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
  4960. {3:0 }{2:0 statusline5 }|
  4961. 13 |
  4962. ]],
  4963. })
  4964. command('let g:status=1')
  4965. api.nvim__redraw({ win = 0, statusline = true })
  4966. screen:expect({
  4967. grid = [[
  4968. {2:tabline3 }|
  4969. {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
  4970. {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
  4971. {3:1 }{2:0 statusline5 }|
  4972. 13 |
  4973. ]],
  4974. })
  4975. -- Can update status widget for a specific buffer
  4976. command('let g:status=2')
  4977. api.nvim__redraw({ buf = 0, statusline = true })
  4978. screen:expect({
  4979. grid = [[
  4980. {2:tabline3 }|
  4981. {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
  4982. {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
  4983. {3:2 }{2:2 statusline5 }|
  4984. 13 |
  4985. ]],
  4986. })
  4987. -- valid = true does not draw any lines on its own
  4988. exec_lua([[
  4989. _G.lines = 0
  4990. ns = vim.api.nvim_create_namespace('')
  4991. vim.api.nvim_set_decoration_provider(ns, {
  4992. on_win = function()
  4993. if _G.do_win then
  4994. vim.api.nvim_buf_set_extmark(0, ns, 0, 0, { hl_group = 'IncSearch', end_col = 6 })
  4995. end
  4996. end,
  4997. on_line = function()
  4998. _G.lines = _G.lines + 1
  4999. end,
  5000. })
  5001. ]])
  5002. local lines = exec_lua('return lines')
  5003. api.nvim__redraw({ buf = 0, valid = true, flush = true })
  5004. eq(lines, exec_lua('return _G.lines'))
  5005. -- valid = false does
  5006. api.nvim__redraw({ buf = 0, valid = false, flush = true })
  5007. neq(lines, exec_lua('return _G.lines'))
  5008. -- valid = true does redraw lines if affected by on_win callback
  5009. exec_lua('_G.do_win = true')
  5010. api.nvim__redraw({ buf = 0, valid = true, flush = true })
  5011. screen:expect({
  5012. grid = [[
  5013. {2:tabline3 }|
  5014. {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
  5015. {8:statuscolumn2}{2:^foobaz} │{8:statuscolumn2}{2:foobaz}│{8:statuscolumn}foobar |
  5016. {3:2 }{2:2 statusline5 }|
  5017. 13 |
  5018. ]],
  5019. })
  5020. end)
  5021. it('nvim__redraw range parameter', function()
  5022. Screen.new(10, 5)
  5023. fn.setline(1, fn.range(4))
  5024. exec_lua([[
  5025. _G.lines_list = {}
  5026. ns = vim.api.nvim_create_namespace('')
  5027. vim.api.nvim_set_decoration_provider(ns, {
  5028. on_win = function()
  5029. end,
  5030. on_line = function(_, _, _, line)
  5031. table.insert(_G.lines_list, line)
  5032. end,
  5033. })
  5034. function _G.get_lines()
  5035. local lines = _G.lines_list
  5036. _G.lines_list = {}
  5037. return lines
  5038. end
  5039. ]])
  5040. api.nvim__redraw({ flush = true, valid = false })
  5041. exec_lua('_G.get_lines()')
  5042. local actual_lines = {}
  5043. local function test(range)
  5044. api.nvim__redraw({ win = 0, range = range })
  5045. table.insert(actual_lines, exec_lua('return _G.get_lines()'))
  5046. end
  5047. test({ 0, -1 })
  5048. test({ 2, 2 ^ 31 })
  5049. test({ 2, 2 ^ 32 })
  5050. test({ 2 ^ 31 - 1, 2 })
  5051. test({ 2 ^ 32 - 1, 2 })
  5052. local expected_lines = {
  5053. { 0, 1, 2, 3 },
  5054. { 2, 3 },
  5055. { 2, 3 },
  5056. {},
  5057. {},
  5058. }
  5059. eq(expected_lines, actual_lines)
  5060. n.assert_alive()
  5061. end)
  5062. end)