vim_spec.lua 139 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028
  1. local helpers = require('test.functional.helpers')(after_each)
  2. local Screen = require('test.functional.ui.screen')
  3. local lfs = require('lfs')
  4. local luv = require('luv')
  5. local fmt = string.format
  6. local assert_alive = helpers.assert_alive
  7. local NIL = helpers.NIL
  8. local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq
  9. local command = helpers.command
  10. local exec = helpers.exec
  11. local eval = helpers.eval
  12. local expect = helpers.expect
  13. local funcs = helpers.funcs
  14. local iswin = helpers.iswin
  15. local meths = helpers.meths
  16. local matches = helpers.matches
  17. local pesc = helpers.pesc
  18. local mkdir_p = helpers.mkdir_p
  19. local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed
  20. local is_os = helpers.is_os
  21. local parse_context = helpers.parse_context
  22. local request = helpers.request
  23. local rmdir = helpers.rmdir
  24. local source = helpers.source
  25. local next_msg = helpers.next_msg
  26. local tmpname = helpers.tmpname
  27. local write_file = helpers.write_file
  28. local exec_lua = helpers.exec_lua
  29. local exc_exec = helpers.exc_exec
  30. local insert = helpers.insert
  31. local expect_exit = helpers.expect_exit
  32. local pcall_err = helpers.pcall_err
  33. local format_string = helpers.format_string
  34. local intchar2lua = helpers.intchar2lua
  35. local mergedicts_copy = helpers.mergedicts_copy
  36. local endswith = helpers.endswith
  37. describe('API', function()
  38. before_each(clear)
  39. it('validates requests', function()
  40. -- RPC
  41. matches('Invalid method: bogus$',
  42. pcall_err(request, 'bogus'))
  43. matches('Invalid method: … の り 。…$',
  44. pcall_err(request, '… の り 。…'))
  45. matches('Invalid method: <empty>$',
  46. pcall_err(request, ''))
  47. -- Non-RPC: rpcrequest(v:servername) uses internal channel.
  48. matches('Invalid method: … の り 。…$',
  49. pcall_err(request, 'nvim_eval',
  50. [=[rpcrequest(sockconnect('pipe', v:servername, {'rpc':1}), '… の り 。…')]=]))
  51. matches('Invalid method: bogus$',
  52. pcall_err(request, 'nvim_eval',
  53. [=[rpcrequest(sockconnect('pipe', v:servername, {'rpc':1}), 'bogus')]=]))
  54. -- XXX: This must be the last one, else next one will fail:
  55. -- "Packer instance already working. Use another Packer ..."
  56. matches("can't serialize object$",
  57. pcall_err(request, nil))
  58. end)
  59. it('handles errors in async requests', function()
  60. local error_types = meths.get_api_info()[2].error_types
  61. nvim_async('bogus')
  62. eq({'notification', 'nvim_error_event',
  63. {error_types.Exception.id, 'Invalid method: nvim_bogus'}}, next_msg())
  64. -- error didn't close channel.
  65. assert_alive()
  66. end)
  67. it('failed async request emits nvim_error_event', function()
  68. local error_types = meths.get_api_info()[2].error_types
  69. nvim_async('command', 'bogus')
  70. eq({'notification', 'nvim_error_event',
  71. {error_types.Exception.id, 'Vim:E492: Not an editor command: bogus'}},
  72. next_msg())
  73. -- error didn't close channel.
  74. assert_alive()
  75. end)
  76. it('does not set CA_COMMAND_BUSY #7254', function()
  77. nvim('command', 'split')
  78. nvim('command', 'autocmd WinEnter * startinsert')
  79. nvim('command', 'wincmd w')
  80. eq({mode='i', blocking=false}, nvim("get_mode"))
  81. end)
  82. describe('nvim_exec', function()
  83. it('one-line input', function()
  84. nvim('exec', "let x1 = 'a'", false)
  85. eq('a', nvim('get_var', 'x1'))
  86. end)
  87. it(':verbose set {option}?', function()
  88. nvim('exec', 'set nowrap', false)
  89. eq('nowrap\n\tLast set from anonymous :source',
  90. nvim('exec', 'verbose set wrap?', true))
  91. -- Using script var to force creation of a script item
  92. nvim('exec', [[
  93. let s:a = 1
  94. set nowrap
  95. ]], false)
  96. eq('nowrap\n\tLast set from anonymous :source (script id 1)',
  97. nvim('exec', 'verbose set wrap?', true))
  98. end)
  99. it('multiline input', function()
  100. -- Heredoc + empty lines.
  101. nvim('exec', "let x2 = 'a'\n", false)
  102. eq('a', nvim('get_var', 'x2'))
  103. nvim('exec','lua <<EOF\n\n\n\ny=3\n\n\nEOF', false)
  104. eq(3, nvim('eval', "luaeval('y')"))
  105. eq('', nvim('exec', 'lua <<EOF\ny=3\nEOF', false))
  106. eq(3, nvim('eval', "luaeval('y')"))
  107. -- Multiple statements
  108. nvim('exec', 'let x1=1\nlet x2=2\nlet x3=3\n', false)
  109. eq(1, nvim('eval', 'x1'))
  110. eq(2, nvim('eval', 'x2'))
  111. eq(3, nvim('eval', 'x3'))
  112. -- Functions
  113. nvim('exec', 'function Foo()\ncall setline(1,["xxx"])\nendfunction', false)
  114. eq('', nvim('get_current_line'))
  115. nvim('exec', 'call Foo()', false)
  116. eq('xxx', nvim('get_current_line'))
  117. -- Autocmds
  118. nvim('exec','autocmd BufAdd * :let x1 = "Hello"', false)
  119. nvim('command', 'new foo')
  120. eq('Hello', request('nvim_eval', 'g:x1'))
  121. -- Line continuations
  122. nvim('exec', [[
  123. let abc = #{
  124. \ a: 1,
  125. "\ b: 2,
  126. \ c: 3
  127. \ }]], false)
  128. eq({a = 1, c = 3}, request('nvim_eval', 'g:abc'))
  129. -- try no spaces before continuations to catch off-by-one error
  130. nvim('exec', 'let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', false)
  131. eq({a = 98}, request('nvim_eval', 'g:ab'))
  132. -- Script scope (s:)
  133. eq('ahoy! script-scoped varrrrr', nvim('exec', [[
  134. let s:pirate = 'script-scoped varrrrr'
  135. function! s:avast_ye_hades(s) abort
  136. return a:s .. ' ' .. s:pirate
  137. endfunction
  138. echo <sid>avast_ye_hades('ahoy!')
  139. ]], true))
  140. eq('ahoy! script-scoped varrrrr', nvim('exec', [[
  141. let s:pirate = 'script-scoped varrrrr'
  142. function! Avast_ye_hades(s) abort
  143. return a:s .. ' ' .. s:pirate
  144. endfunction
  145. echo nvim_exec('echo Avast_ye_hades(''ahoy!'')', 1)
  146. ]], true))
  147. eq('Vim(call):E5555: API call: Vim(echo):E121: Undefined variable: s:pirate',
  148. pcall_err(request, 'nvim_exec', [[
  149. let s:pirate = 'script-scoped varrrrr'
  150. call nvim_exec('echo s:pirate', 1)
  151. ]], false))
  152. -- Script items are created only on script var access
  153. eq('1\n0', nvim('exec', [[
  154. echo expand("<SID>")->empty()
  155. let s:a = 123
  156. echo expand("<SID>")->empty()
  157. ]], true))
  158. eq('1\n0', nvim('exec', [[
  159. echo expand("<SID>")->empty()
  160. function s:a() abort
  161. endfunction
  162. echo expand("<SID>")->empty()
  163. ]], true))
  164. end)
  165. it('non-ASCII input', function()
  166. nvim('exec', [=[
  167. new
  168. exe "normal! i ax \n Ax "
  169. :%s/ax/--a1234--/g | :%s/Ax/--A1234--/g
  170. ]=], false)
  171. nvim('command', '1')
  172. eq(' --a1234-- ', nvim('get_current_line'))
  173. nvim('command', '2')
  174. eq(' --A1234-- ', nvim('get_current_line'))
  175. nvim('exec', [[
  176. new
  177. call setline(1,['xxx'])
  178. call feedkeys('r')
  179. call feedkeys('ñ', 'xt')
  180. ]], false)
  181. eq('ñxx', nvim('get_current_line'))
  182. end)
  183. it('execution error', function()
  184. eq('Vim:E492: Not an editor command: bogus_command',
  185. pcall_err(request, 'nvim_exec', 'bogus_command', false))
  186. eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated.
  187. eq('', eval('v:exception'))
  188. eq('Vim(buffer):E86: Buffer 23487 does not exist',
  189. pcall_err(request, 'nvim_exec', 'buffer 23487', false))
  190. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  191. eq('', eval('v:exception'))
  192. end)
  193. it('recursion', function()
  194. local fname = tmpname()
  195. write_file(fname, 'let x1 = "set from :source file"\n')
  196. -- nvim_exec
  197. -- :source
  198. -- nvim_exec
  199. request('nvim_exec', [[
  200. let x2 = substitute('foo','o','X','g')
  201. let x4 = 'should be overwritten'
  202. call nvim_exec("source ]]..fname..[[\nlet x3 = substitute('foo','foo','set by recursive nvim_exec','g')\nlet x5='overwritten'\nlet x4=x5\n", v:false)
  203. ]], false)
  204. eq('set from :source file', request('nvim_get_var', 'x1'))
  205. eq('fXX', request('nvim_get_var', 'x2'))
  206. eq('set by recursive nvim_exec', request('nvim_get_var', 'x3'))
  207. eq('overwritten', request('nvim_get_var', 'x4'))
  208. eq('overwritten', request('nvim_get_var', 'x5'))
  209. os.remove(fname)
  210. end)
  211. it('traceback', function()
  212. local fname = tmpname()
  213. write_file(fname, 'echo "hello"\n')
  214. local sourcing_fname = tmpname()
  215. write_file(sourcing_fname, 'call nvim_exec("source '..fname..'", v:false)\n')
  216. meths.exec('set verbose=2', false)
  217. local traceback_output = 'line 0: sourcing "'..sourcing_fname..'"\n'..
  218. 'line 0: sourcing "'..fname..'"\n'..
  219. 'hello\n'..
  220. 'finished sourcing '..fname..'\n'..
  221. 'continuing in nvim_exec() called at '..sourcing_fname..':1\n'..
  222. 'finished sourcing '..sourcing_fname..'\n'..
  223. 'continuing in nvim_exec() called at nvim_exec():0'
  224. eq(traceback_output,
  225. meths.exec('call nvim_exec("source '..sourcing_fname..'", v:false)', true))
  226. os.remove(fname)
  227. os.remove(sourcing_fname)
  228. end)
  229. it('returns output', function()
  230. eq('this is spinal tap',
  231. nvim('exec', 'lua <<EOF\n\n\nprint("this is spinal tap")\n\n\nEOF', true))
  232. eq('', nvim('exec', 'echo', true))
  233. eq('foo 42', nvim('exec', 'echo "foo" 42', true))
  234. end)
  235. it('displays messages when output=false', function()
  236. local screen = Screen.new(40, 8)
  237. screen:attach()
  238. screen:set_default_attr_ids({
  239. [0] = {bold=true, foreground=Screen.colors.Blue},
  240. })
  241. meths.exec("echo 'hello'", false)
  242. screen:expect{grid=[[
  243. ^ |
  244. {0:~ }|
  245. {0:~ }|
  246. {0:~ }|
  247. {0:~ }|
  248. {0:~ }|
  249. {0:~ }|
  250. hello |
  251. ]]}
  252. end)
  253. it('doesn\'t display messages when output=true', function()
  254. local screen = Screen.new(40, 6)
  255. screen:attach()
  256. screen:set_default_attr_ids({
  257. [0] = {bold=true, foreground=Screen.colors.Blue},
  258. })
  259. meths.exec("echo 'hello'", true)
  260. screen:expect{grid=[[
  261. ^ |
  262. {0:~ }|
  263. {0:~ }|
  264. {0:~ }|
  265. {0:~ }|
  266. |
  267. ]]}
  268. exec([[
  269. func Print()
  270. call nvim_exec('echo "hello"', v:true)
  271. endfunc
  272. ]])
  273. feed([[:echon 1 | call Print() | echon 5<CR>]])
  274. screen:expect{grid=[[
  275. ^ |
  276. {0:~ }|
  277. {0:~ }|
  278. {0:~ }|
  279. {0:~ }|
  280. 15 |
  281. ]]}
  282. end)
  283. end)
  284. describe('nvim_command', function()
  285. it('works', function()
  286. local fname = tmpname()
  287. nvim('command', 'new')
  288. nvim('command', 'edit '..fname)
  289. nvim('command', 'normal itesting\napi')
  290. nvim('command', 'w')
  291. local f = io.open(fname)
  292. ok(f ~= nil)
  293. if is_os('win') then
  294. eq('testing\r\napi\r\n', f:read('*a'))
  295. else
  296. eq('testing\napi\n', f:read('*a'))
  297. end
  298. f:close()
  299. os.remove(fname)
  300. end)
  301. it('VimL validation error: fails with specific error', function()
  302. local status, rv = pcall(nvim, "command", "bogus_command")
  303. eq(false, status) -- nvim_command() failed.
  304. eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned.
  305. eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated.
  306. eq('', eval('v:exception'))
  307. end)
  308. it('VimL execution error: fails with specific error', function()
  309. local status, rv = pcall(nvim, "command", "buffer 23487")
  310. eq(false, status) -- nvim_command() failed.
  311. eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*"))
  312. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  313. eq('', eval('v:exception'))
  314. end)
  315. end)
  316. describe('nvim_command_output', function()
  317. it('does not induce hit-enter prompt', function()
  318. nvim("ui_attach", 80, 20, {})
  319. -- Induce a hit-enter prompt use nvim_input (non-blocking).
  320. nvim('command', 'set cmdheight=1')
  321. nvim('input', [[:echo "hi\nhi2"<CR>]])
  322. -- Verify hit-enter prompt.
  323. eq({mode='r', blocking=true}, nvim("get_mode"))
  324. nvim('input', [[<C-c>]])
  325. -- Verify NO hit-enter prompt.
  326. nvim('command_output', [[echo "hi\nhi2"]])
  327. eq({mode='n', blocking=false}, nvim("get_mode"))
  328. end)
  329. it('captures command output', function()
  330. eq('this is\nspinal tap',
  331. nvim('command_output', [[echo "this is\nspinal tap"]]))
  332. eq('no line ending!',
  333. nvim('command_output', [[echon "no line ending!"]]))
  334. end)
  335. it('captures empty command output', function()
  336. eq('', nvim('command_output', 'echo'))
  337. end)
  338. it('captures single-char command output', function()
  339. eq('x', nvim('command_output', 'echo "x"'))
  340. end)
  341. it('captures multiple commands', function()
  342. eq('foo\n 1 %a "[No Name]" line 1',
  343. nvim('command_output', 'echo "foo" | ls'))
  344. end)
  345. it('captures nested execute()', function()
  346. eq('\nnested1\nnested2\n 1 %a "[No Name]" line 1',
  347. nvim('command_output',
  348. [[echo execute('echo "nested1\nnested2"') | ls]]))
  349. end)
  350. it('captures nested nvim_command_output()', function()
  351. eq('nested1\nnested2\n 1 %a "[No Name]" line 1',
  352. nvim('command_output',
  353. [[echo nvim_command_output('echo "nested1\nnested2"') | ls]]))
  354. end)
  355. it('returns shell |:!| output', function()
  356. local win_lf = iswin() and '\r' or ''
  357. eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]]))
  358. end)
  359. it('VimL validation error: fails with specific error', function()
  360. local status, rv = pcall(nvim, "command_output", "bogus commannnd")
  361. eq(false, status) -- nvim_command_output() failed.
  362. eq("E492: Not an editor command: bogus commannnd",
  363. string.match(rv, "E%d*:.*"))
  364. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  365. -- Verify NO hit-enter prompt.
  366. eq({mode='n', blocking=false}, nvim("get_mode"))
  367. end)
  368. it('VimL execution error: fails with specific error', function()
  369. local status, rv = pcall(nvim, "command_output", "buffer 42")
  370. eq(false, status) -- nvim_command_output() failed.
  371. eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*"))
  372. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  373. -- Verify NO hit-enter prompt.
  374. eq({mode='n', blocking=false}, nvim("get_mode"))
  375. end)
  376. it('Does not cause heap buffer overflow with large output', function()
  377. eq(eval('string(range(1000000))'),
  378. nvim('command_output', 'echo range(1000000)'))
  379. end)
  380. end)
  381. describe('nvim_eval', function()
  382. it('works', function()
  383. nvim('command', 'let g:v1 = "a"')
  384. nvim('command', 'let g:v2 = [1, 2, {"v3": 3}]')
  385. eq({v1 = 'a', v2 = { 1, 2, { v3 = 3 } } }, nvim('eval', 'g:'))
  386. end)
  387. it('handles NULL-initialized strings correctly', function()
  388. eq(1, nvim('eval',"matcharg(1) == ['', '']"))
  389. eq({'', ''}, nvim('eval','matcharg(1)'))
  390. end)
  391. it('works under deprecated name', function()
  392. eq(2, request("vim_eval", "1+1"))
  393. end)
  394. it("VimL error: returns error details, does NOT update v:errmsg", function()
  395. eq('Vim:E121: Undefined variable: bogus',
  396. pcall_err(request, 'nvim_eval', 'bogus expression'))
  397. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  398. end)
  399. end)
  400. describe('nvim_call_function', function()
  401. it('works', function()
  402. nvim('call_function', 'setqflist', { { { filename = 'something', lnum = 17 } }, 'r' })
  403. eq(17, nvim('call_function', 'getqflist', {})[1].lnum)
  404. eq(17, nvim('call_function', 'eval', {17}))
  405. eq('foo', nvim('call_function', 'simplify', {'this/./is//redundant/../../../foo'}))
  406. end)
  407. it("VimL validation error: returns specific error, does NOT update v:errmsg", function()
  408. eq('Vim:E117: Unknown function: bogus function',
  409. pcall_err(request, 'nvim_call_function', 'bogus function', {'arg1'}))
  410. eq('Vim:E119: Not enough arguments for function: atan',
  411. pcall_err(request, 'nvim_call_function', 'atan', {}))
  412. eq('', eval('v:exception'))
  413. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  414. end)
  415. it("VimL error: returns error details, does NOT update v:errmsg", function()
  416. eq('Vim:E808: Number or Float required',
  417. pcall_err(request, 'nvim_call_function', 'atan', {'foo'}))
  418. eq('Vim:Invalid channel stream "xxx"',
  419. pcall_err(request, 'nvim_call_function', 'chanclose', {999, 'xxx'}))
  420. eq('Vim:E900: Invalid channel id',
  421. pcall_err(request, 'nvim_call_function', 'chansend', {999, 'foo'}))
  422. eq('', eval('v:exception'))
  423. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  424. end)
  425. it("VimL exception: returns exception details, does NOT update v:errmsg", function()
  426. source([[
  427. function! Foo() abort
  428. throw 'wtf'
  429. endfunction
  430. ]])
  431. eq('wtf', pcall_err(request, 'nvim_call_function', 'Foo', {}))
  432. eq('', eval('v:exception'))
  433. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
  434. end)
  435. it('validates args', function()
  436. 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' }
  437. source([[
  438. function! Foo(...) abort
  439. echo a:000
  440. endfunction
  441. ]])
  442. -- E740
  443. eq('Function called with too many arguments',
  444. pcall_err(request, 'nvim_call_function', 'Foo', too_many_args))
  445. end)
  446. end)
  447. describe('nvim_call_dict_function', function()
  448. it('invokes VimL dict function', function()
  449. source([[
  450. function! F(name) dict
  451. return self.greeting.', '.a:name.'!'
  452. endfunction
  453. let g:test_dict_fn = { 'greeting':'Hello', 'F':function('F') }
  454. let g:test_dict_fn2 = { 'greeting':'Hi' }
  455. function g:test_dict_fn2.F2(name)
  456. return self.greeting.', '.a:name.' ...'
  457. endfunction
  458. ]])
  459. -- :help Dictionary-function
  460. eq('Hello, World!', nvim('call_dict_function', 'g:test_dict_fn', 'F', {'World'}))
  461. -- Funcref is sent as NIL over RPC.
  462. eq({ greeting = 'Hello', F = NIL }, nvim('get_var', 'test_dict_fn'))
  463. -- :help numbered-function
  464. eq('Hi, Moon ...', nvim('call_dict_function', 'g:test_dict_fn2', 'F2', {'Moon'}))
  465. -- Funcref is sent as NIL over RPC.
  466. eq({ greeting = 'Hi', F2 = NIL }, nvim('get_var', 'test_dict_fn2'))
  467. -- Function specified via RPC dict.
  468. source('function! G() dict\n return "@".(self.result)."@"\nendfunction')
  469. eq('@it works@', nvim('call_dict_function', { result = 'it works', G = 'G'}, 'G', {}))
  470. end)
  471. it('validates args', function()
  472. command('let g:d={"baz":"zub","meep":[]}')
  473. eq('Not found: bogus',
  474. pcall_err(request, 'nvim_call_dict_function', 'g:d', 'bogus', {1,2}))
  475. eq('Not a function: baz',
  476. pcall_err(request, 'nvim_call_dict_function', 'g:d', 'baz', {1,2}))
  477. eq('Not a function: meep',
  478. pcall_err(request, 'nvim_call_dict_function', 'g:d', 'meep', {1,2}))
  479. eq('Vim:E117: Unknown function: f',
  480. pcall_err(request, 'nvim_call_dict_function', { f = '' }, 'f', {1,2}))
  481. eq('Not a function: f',
  482. pcall_err(request, 'nvim_call_dict_function', "{ 'f': '' }", 'f', {1,2}))
  483. eq('dict argument type must be String or Dictionary',
  484. pcall_err(request, 'nvim_call_dict_function', 42, 'f', {1,2}))
  485. eq('Failed to evaluate dict expression',
  486. pcall_err(request, 'nvim_call_dict_function', 'foo', 'f', {1,2}))
  487. eq('dict not found',
  488. pcall_err(request, 'nvim_call_dict_function', '42', 'f', {1,2}))
  489. eq('Invalid (empty) function name',
  490. pcall_err(request, 'nvim_call_dict_function', "{ 'f': '' }", '', {1,2}))
  491. end)
  492. end)
  493. describe('nvim_set_current_dir', function()
  494. local start_dir
  495. before_each(function()
  496. clear()
  497. funcs.mkdir("Xtestdir")
  498. start_dir = funcs.getcwd()
  499. end)
  500. after_each(function()
  501. helpers.rmdir("Xtestdir")
  502. end)
  503. it('works', function()
  504. meths.set_current_dir("Xtestdir")
  505. eq(funcs.getcwd(), start_dir .. helpers.get_pathsep() .. "Xtestdir")
  506. end)
  507. it('sets previous directory', function()
  508. meths.set_current_dir("Xtestdir")
  509. meths.exec('cd -', false)
  510. eq(funcs.getcwd(), start_dir)
  511. end)
  512. end)
  513. describe('nvim_exec_lua', function()
  514. it('works', function()
  515. meths.exec_lua('vim.api.nvim_set_var("test", 3)', {})
  516. eq(3, meths.get_var('test'))
  517. eq(17, meths.exec_lua('a, b = ...\nreturn a + b', {10,7}))
  518. eq(NIL, meths.exec_lua('function xx(a,b)\nreturn a..b\nend',{}))
  519. eq("xy", meths.exec_lua('return xx(...)', {'x','y'}))
  520. -- Deprecated name: nvim_execute_lua.
  521. eq("xy", meths.execute_lua('return xx(...)', {'x','y'}))
  522. end)
  523. it('reports errors', function()
  524. eq([[Error loading lua: [string "<nvim>"]:0: '=' expected near '+']],
  525. pcall_err(meths.exec_lua, 'a+*b', {}))
  526. eq([[Error loading lua: [string "<nvim>"]:0: unexpected symbol near '1']],
  527. pcall_err(meths.exec_lua, '1+2', {}))
  528. eq([[Error loading lua: [string "<nvim>"]:0: unexpected symbol]],
  529. pcall_err(meths.exec_lua, 'aa=bb\0', {}))
  530. eq([[Error executing lua: [string "<nvim>"]:0: attempt to call global 'bork' (a nil value)]],
  531. pcall_err(meths.exec_lua, 'bork()', {}))
  532. eq('Error executing lua: [string "<nvim>"]:0: did\nthe\nfail',
  533. pcall_err(meths.exec_lua, 'error("did\\nthe\\nfail")', {}))
  534. end)
  535. it('uses native float values', function()
  536. eq(2.5, meths.exec_lua("return select(1, ...)", {2.5}))
  537. eq("2.5", meths.exec_lua("return vim.inspect(...)", {2.5}))
  538. -- "special" float values are still accepted as return values.
  539. eq(2.5, meths.exec_lua("return vim.api.nvim_eval('2.5')", {}))
  540. eq("{\n [false] = 2.5,\n [true] = 3\n}", meths.exec_lua("return vim.inspect(vim.api.nvim_eval('2.5'))", {}))
  541. end)
  542. end)
  543. describe('nvim_notify', function()
  544. it('can notify a info message', function()
  545. nvim("notify", "hello world", 2, {})
  546. end)
  547. it('can be overridden', function()
  548. command("lua vim.notify = function(...) return 42 end")
  549. eq(42, meths.exec_lua("return vim.notify('Hello world')", {}))
  550. nvim("notify", "hello world", 4, {})
  551. end)
  552. end)
  553. describe('nvim_input', function()
  554. it("VimL error: does NOT fail, updates v:errmsg", function()
  555. local status, _ = pcall(nvim, "input", ":call bogus_fn()<CR>")
  556. local v_errnum = string.match(nvim("eval", "v:errmsg"), "E%d*:")
  557. eq(true, status) -- nvim_input() did not fail.
  558. eq("E117:", v_errnum) -- v:errmsg was updated.
  559. end)
  560. it('does not crash even if trans_special result is largest #11788, #12287', function()
  561. command("call nvim_input('<M-'.nr2char(0x40000000).'>')")
  562. eq(1, eval('1'))
  563. end)
  564. end)
  565. describe('nvim_paste', function()
  566. it('validates args', function()
  567. eq('Invalid phase: -2',
  568. pcall_err(request, 'nvim_paste', 'foo', true, -2))
  569. eq('Invalid phase: 4',
  570. pcall_err(request, 'nvim_paste', 'foo', true, 4))
  571. end)
  572. local function run_streamed_paste_tests()
  573. it('stream: multiple chunks form one undo-block', function()
  574. nvim('paste', '1/chunk 1 (start)\n', true, 1)
  575. nvim('paste', '1/chunk 2 (end)\n', true, 3)
  576. local expected1 = [[
  577. 1/chunk 1 (start)
  578. 1/chunk 2 (end)
  579. ]]
  580. expect(expected1)
  581. nvim('paste', '2/chunk 1 (start)\n', true, 1)
  582. nvim('paste', '2/chunk 2\n', true, 2)
  583. expect([[
  584. 1/chunk 1 (start)
  585. 1/chunk 2 (end)
  586. 2/chunk 1 (start)
  587. 2/chunk 2
  588. ]])
  589. nvim('paste', '2/chunk 3\n', true, 2)
  590. nvim('paste', '2/chunk 4 (end)\n', true, 3)
  591. expect([[
  592. 1/chunk 1 (start)
  593. 1/chunk 2 (end)
  594. 2/chunk 1 (start)
  595. 2/chunk 2
  596. 2/chunk 3
  597. 2/chunk 4 (end)
  598. ]])
  599. feed('u') -- Undo.
  600. expect(expected1)
  601. end)
  602. it('stream: Insert mode', function()
  603. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  604. feed('afoo<Esc>u')
  605. feed('i')
  606. nvim('paste', 'aaaaaa', false, 1)
  607. nvim('paste', 'bbbbbb', false, 2)
  608. nvim('paste', 'cccccc', false, 2)
  609. nvim('paste', 'dddddd', false, 3)
  610. expect('aaaaaabbbbbbccccccdddddd')
  611. feed('<Esc>u')
  612. expect('')
  613. end)
  614. describe('stream: Normal mode', function()
  615. describe('on empty line', function()
  616. before_each(function()
  617. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  618. feed('afoo<Esc>u')
  619. end)
  620. after_each(function()
  621. feed('u')
  622. expect('')
  623. end)
  624. it('pasting one line', function()
  625. nvim('paste', 'aaaaaa', false, 1)
  626. nvim('paste', 'bbbbbb', false, 2)
  627. nvim('paste', 'cccccc', false, 2)
  628. nvim('paste', 'dddddd', false, 3)
  629. expect('aaaaaabbbbbbccccccdddddd')
  630. end)
  631. it('pasting multiple lines', function()
  632. nvim('paste', 'aaaaaa\n', false, 1)
  633. nvim('paste', 'bbbbbb\n', false, 2)
  634. nvim('paste', 'cccccc\n', false, 2)
  635. nvim('paste', 'dddddd', false, 3)
  636. expect([[
  637. aaaaaa
  638. bbbbbb
  639. cccccc
  640. dddddd]])
  641. end)
  642. end)
  643. describe('not at the end of a line', function()
  644. before_each(function()
  645. feed('i||<Esc>')
  646. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  647. feed('afoo<Esc>u')
  648. feed('0')
  649. end)
  650. after_each(function()
  651. feed('u')
  652. expect('||')
  653. end)
  654. it('pasting one line', function()
  655. nvim('paste', 'aaaaaa', false, 1)
  656. nvim('paste', 'bbbbbb', false, 2)
  657. nvim('paste', 'cccccc', false, 2)
  658. nvim('paste', 'dddddd', false, 3)
  659. expect('|aaaaaabbbbbbccccccdddddd|')
  660. end)
  661. it('pasting multiple lines', function()
  662. nvim('paste', 'aaaaaa\n', false, 1)
  663. nvim('paste', 'bbbbbb\n', false, 2)
  664. nvim('paste', 'cccccc\n', false, 2)
  665. nvim('paste', 'dddddd', false, 3)
  666. expect([[
  667. |aaaaaa
  668. bbbbbb
  669. cccccc
  670. dddddd|]])
  671. end)
  672. end)
  673. describe('at the end of a line', function()
  674. before_each(function()
  675. feed('i||<Esc>')
  676. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  677. feed('afoo<Esc>u')
  678. feed('2|')
  679. end)
  680. after_each(function()
  681. feed('u')
  682. expect('||')
  683. end)
  684. it('pasting one line', function()
  685. nvim('paste', 'aaaaaa', false, 1)
  686. nvim('paste', 'bbbbbb', false, 2)
  687. nvim('paste', 'cccccc', false, 2)
  688. nvim('paste', 'dddddd', false, 3)
  689. expect('||aaaaaabbbbbbccccccdddddd')
  690. end)
  691. it('pasting multiple lines', function()
  692. nvim('paste', 'aaaaaa\n', false, 1)
  693. nvim('paste', 'bbbbbb\n', false, 2)
  694. nvim('paste', 'cccccc\n', false, 2)
  695. nvim('paste', 'dddddd', false, 3)
  696. expect([[
  697. ||aaaaaa
  698. bbbbbb
  699. cccccc
  700. dddddd]])
  701. end)
  702. end)
  703. end)
  704. describe('stream: Visual mode', function()
  705. describe('neither end at the end of a line', function()
  706. before_each(function()
  707. feed('i|xxx<CR>xxx|<Esc>')
  708. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  709. feed('afoo<Esc>u')
  710. feed('3|vhk')
  711. end)
  712. after_each(function()
  713. feed('u')
  714. expect([[
  715. |xxx
  716. xxx|]])
  717. end)
  718. it('with non-empty chunks', function()
  719. nvim('paste', 'aaaaaa', false, 1)
  720. nvim('paste', 'bbbbbb', false, 2)
  721. nvim('paste', 'cccccc', false, 2)
  722. nvim('paste', 'dddddd', false, 3)
  723. expect('|aaaaaabbbbbbccccccdddddd|')
  724. end)
  725. it('with empty first chunk', function()
  726. nvim('paste', '', false, 1)
  727. nvim('paste', 'bbbbbb', false, 2)
  728. nvim('paste', 'cccccc', false, 2)
  729. nvim('paste', 'dddddd', false, 3)
  730. expect('|bbbbbbccccccdddddd|')
  731. end)
  732. it('with all chunks empty', function()
  733. nvim('paste', '', false, 1)
  734. nvim('paste', '', false, 2)
  735. nvim('paste', '', false, 2)
  736. nvim('paste', '', false, 3)
  737. expect('||')
  738. end)
  739. end)
  740. describe('cursor at the end of a line', function()
  741. before_each(function()
  742. feed('i||xxx<CR>xxx<Esc>')
  743. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  744. feed('afoo<Esc>u')
  745. feed('3|vko')
  746. end)
  747. after_each(function()
  748. feed('u')
  749. expect([[
  750. ||xxx
  751. xxx]])
  752. end)
  753. it('with non-empty chunks', function()
  754. nvim('paste', 'aaaaaa', false, 1)
  755. nvim('paste', 'bbbbbb', false, 2)
  756. nvim('paste', 'cccccc', false, 2)
  757. nvim('paste', 'dddddd', false, 3)
  758. expect('||aaaaaabbbbbbccccccdddddd')
  759. end)
  760. it('with empty first chunk', function()
  761. nvim('paste', '', false, 1)
  762. nvim('paste', 'bbbbbb', false, 2)
  763. nvim('paste', 'cccccc', false, 2)
  764. nvim('paste', 'dddddd', false, 3)
  765. expect('||bbbbbbccccccdddddd')
  766. end)
  767. end)
  768. describe('other end at the end of a line', function()
  769. before_each(function()
  770. feed('i||xxx<CR>xxx<Esc>')
  771. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  772. feed('afoo<Esc>u')
  773. feed('3|vk')
  774. end)
  775. after_each(function()
  776. feed('u')
  777. expect([[
  778. ||xxx
  779. xxx]])
  780. end)
  781. it('with non-empty chunks', function()
  782. nvim('paste', 'aaaaaa', false, 1)
  783. nvim('paste', 'bbbbbb', false, 2)
  784. nvim('paste', 'cccccc', false, 2)
  785. nvim('paste', 'dddddd', false, 3)
  786. expect('||aaaaaabbbbbbccccccdddddd')
  787. end)
  788. it('with empty first chunk', function()
  789. nvim('paste', '', false, 1)
  790. nvim('paste', 'bbbbbb', false, 2)
  791. nvim('paste', 'cccccc', false, 2)
  792. nvim('paste', 'dddddd', false, 3)
  793. expect('||bbbbbbccccccdddddd')
  794. end)
  795. end)
  796. end)
  797. describe('stream: linewise Visual mode', function()
  798. before_each(function()
  799. feed('i123456789<CR>987654321<CR>123456789<Esc>')
  800. -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
  801. feed('afoo<Esc>u')
  802. end)
  803. after_each(function()
  804. feed('u')
  805. expect([[
  806. 123456789
  807. 987654321
  808. 123456789]])
  809. end)
  810. describe('selecting the start of a file', function()
  811. before_each(function()
  812. feed('ggV')
  813. end)
  814. it('pasting text without final new line', function()
  815. nvim('paste', 'aaaaaa\n', false, 1)
  816. nvim('paste', 'bbbbbb\n', false, 2)
  817. nvim('paste', 'cccccc\n', false, 2)
  818. nvim('paste', 'dddddd', false, 3)
  819. expect([[
  820. aaaaaa
  821. bbbbbb
  822. cccccc
  823. dddddd987654321
  824. 123456789]])
  825. end)
  826. it('pasting text with final new line', function()
  827. nvim('paste', 'aaaaaa\n', false, 1)
  828. nvim('paste', 'bbbbbb\n', false, 2)
  829. nvim('paste', 'cccccc\n', false, 2)
  830. nvim('paste', 'dddddd\n', false, 3)
  831. expect([[
  832. aaaaaa
  833. bbbbbb
  834. cccccc
  835. dddddd
  836. 987654321
  837. 123456789]])
  838. end)
  839. end)
  840. describe('selecting the middle of a file', function()
  841. before_each(function()
  842. feed('2ggV')
  843. end)
  844. it('pasting text without final new line', function()
  845. nvim('paste', 'aaaaaa\n', false, 1)
  846. nvim('paste', 'bbbbbb\n', false, 2)
  847. nvim('paste', 'cccccc\n', false, 2)
  848. nvim('paste', 'dddddd', false, 3)
  849. expect([[
  850. 123456789
  851. aaaaaa
  852. bbbbbb
  853. cccccc
  854. dddddd123456789]])
  855. end)
  856. it('pasting text with final new line', function()
  857. nvim('paste', 'aaaaaa\n', false, 1)
  858. nvim('paste', 'bbbbbb\n', false, 2)
  859. nvim('paste', 'cccccc\n', false, 2)
  860. nvim('paste', 'dddddd\n', false, 3)
  861. expect([[
  862. 123456789
  863. aaaaaa
  864. bbbbbb
  865. cccccc
  866. dddddd
  867. 123456789]])
  868. end)
  869. end)
  870. describe('selecting the end of a file', function()
  871. before_each(function()
  872. feed('3ggV')
  873. end)
  874. it('pasting text without final new line', function()
  875. nvim('paste', 'aaaaaa\n', false, 1)
  876. nvim('paste', 'bbbbbb\n', false, 2)
  877. nvim('paste', 'cccccc\n', false, 2)
  878. nvim('paste', 'dddddd', false, 3)
  879. expect([[
  880. 123456789
  881. 987654321
  882. aaaaaa
  883. bbbbbb
  884. cccccc
  885. dddddd]])
  886. end)
  887. it('pasting text with final new line', function()
  888. nvim('paste', 'aaaaaa\n', false, 1)
  889. nvim('paste', 'bbbbbb\n', false, 2)
  890. nvim('paste', 'cccccc\n', false, 2)
  891. nvim('paste', 'dddddd\n', false, 3)
  892. expect([[
  893. 123456789
  894. 987654321
  895. aaaaaa
  896. bbbbbb
  897. cccccc
  898. dddddd
  899. ]])
  900. end)
  901. end)
  902. describe('selecting the whole file', function()
  903. before_each(function()
  904. feed('ggVG')
  905. end)
  906. it('pasting text without final new line', function()
  907. nvim('paste', 'aaaaaa\n', false, 1)
  908. nvim('paste', 'bbbbbb\n', false, 2)
  909. nvim('paste', 'cccccc\n', false, 2)
  910. nvim('paste', 'dddddd', false, 3)
  911. expect([[
  912. aaaaaa
  913. bbbbbb
  914. cccccc
  915. dddddd]])
  916. end)
  917. it('pasting text with final new line', function()
  918. nvim('paste', 'aaaaaa\n', false, 1)
  919. nvim('paste', 'bbbbbb\n', false, 2)
  920. nvim('paste', 'cccccc\n', false, 2)
  921. nvim('paste', 'dddddd\n', false, 3)
  922. expect([[
  923. aaaaaa
  924. bbbbbb
  925. cccccc
  926. dddddd
  927. ]])
  928. end)
  929. end)
  930. end)
  931. end
  932. describe('without virtualedit,', function()
  933. run_streamed_paste_tests()
  934. end)
  935. describe('with virtualedit=onemore,', function()
  936. before_each(function()
  937. command('set virtualedit=onemore')
  938. end)
  939. run_streamed_paste_tests()
  940. end)
  941. it('non-streaming', function()
  942. -- With final "\n".
  943. nvim('paste', 'line 1\nline 2\nline 3\n', true, -1)
  944. expect([[
  945. line 1
  946. line 2
  947. line 3
  948. ]])
  949. eq({0,4,1,0}, funcs.getpos('.')) -- Cursor follows the paste.
  950. eq(false, nvim('get_option', 'paste'))
  951. command('%delete _')
  952. -- Without final "\n".
  953. nvim('paste', 'line 1\nline 2\nline 3', true, -1)
  954. expect([[
  955. line 1
  956. line 2
  957. line 3]])
  958. eq({0,3,6,0}, funcs.getpos('.'))
  959. command('%delete _')
  960. -- CRLF #10872
  961. nvim('paste', 'line 1\r\nline 2\r\nline 3\r\n', true, -1)
  962. expect([[
  963. line 1
  964. line 2
  965. line 3
  966. ]])
  967. eq({0,4,1,0}, funcs.getpos('.'))
  968. command('%delete _')
  969. -- CRLF without final "\n".
  970. nvim('paste', 'line 1\r\nline 2\r\nline 3\r', true, -1)
  971. expect([[
  972. line 1
  973. line 2
  974. line 3
  975. ]])
  976. eq({0,4,1,0}, funcs.getpos('.'))
  977. command('%delete _')
  978. -- CRLF without final "\r\n".
  979. nvim('paste', 'line 1\r\nline 2\r\nline 3', true, -1)
  980. expect([[
  981. line 1
  982. line 2
  983. line 3]])
  984. eq({0,3,6,0}, funcs.getpos('.'))
  985. command('%delete _')
  986. -- Various other junk.
  987. nvim('paste', 'line 1\r\n\r\rline 2\nline 3\rline 4\r', true, -1)
  988. expect('line 1\n\n\nline 2\nline 3\nline 4\n')
  989. eq({0,7,1,0}, funcs.getpos('.'))
  990. eq(false, nvim('get_option', 'paste'))
  991. end)
  992. it('Replace-mode', function()
  993. -- Within single line
  994. nvim('put', {'aabbccdd', 'eeffgghh', 'iijjkkll'}, "c", true, false)
  995. command('normal l')
  996. command('startreplace')
  997. nvim('paste', '123456', true, -1)
  998. expect([[
  999. a123456d
  1000. eeffgghh
  1001. iijjkkll]])
  1002. command('%delete _')
  1003. -- Across lines
  1004. nvim('put', {'aabbccdd', 'eeffgghh', 'iijjkkll'}, "c", true, false)
  1005. command('normal l')
  1006. command('startreplace')
  1007. nvim('paste', '123\n456', true, -1)
  1008. expect([[
  1009. a123
  1010. 456d
  1011. eeffgghh
  1012. iijjkkll]])
  1013. end)
  1014. it('when searching in Visual mode', function()
  1015. feed('v/')
  1016. nvim('paste', 'aabbccdd', true, -1)
  1017. eq('aabbccdd', funcs.getcmdline())
  1018. expect('')
  1019. end)
  1020. it('mappings are disabled in Cmdline mode', function()
  1021. command('cnoremap a b')
  1022. feed(':')
  1023. nvim('paste', 'a', true, -1)
  1024. eq('a', funcs.getcmdline())
  1025. end)
  1026. it('pasted text is saved in cmdline history when <CR> comes from mapping #20957', function()
  1027. command('cnoremap <CR> <CR>')
  1028. feed(':')
  1029. nvim('paste', 'echo', true, -1)
  1030. eq('', funcs.histget(':'))
  1031. feed('<CR>')
  1032. eq('echo', funcs.histget(':'))
  1033. end)
  1034. it('pasting with empty last chunk in Cmdline mode', function()
  1035. local screen = Screen.new(20, 4)
  1036. screen:attach()
  1037. feed(':')
  1038. nvim('paste', 'Foo', true, 1)
  1039. nvim('paste', '', true, 3)
  1040. screen:expect([[
  1041. |
  1042. ~ |
  1043. ~ |
  1044. :Foo^ |
  1045. ]])
  1046. end)
  1047. it('pasting text with control characters in Cmdline mode', function()
  1048. local screen = Screen.new(20, 4)
  1049. screen:attach()
  1050. feed(':')
  1051. nvim('paste', 'normal! \023\022\006\027', true, -1)
  1052. screen:expect([[
  1053. |
  1054. ~ |
  1055. ~ |
  1056. :normal! ^W^V^F^[^ |
  1057. ]])
  1058. end)
  1059. it('crlf=false does not break lines at CR, CRLF', function()
  1060. nvim('paste', 'line 1\r\n\r\rline 2\nline 3\rline 4\r', false, -1)
  1061. expect('line 1\r\n\r\rline 2\nline 3\rline 4\r')
  1062. eq({0,3,14,0}, funcs.getpos('.'))
  1063. end)
  1064. it('vim.paste() failure', function()
  1065. nvim('exec_lua', 'vim.paste = (function(lines, phase) error("fake fail") end)', {})
  1066. eq([[Error executing lua: [string "<nvim>"]:0: fake fail]],
  1067. pcall_err(request, 'nvim_paste', 'line 1\nline 2\nline 3', false, 1))
  1068. end)
  1069. end)
  1070. describe('nvim_put', function()
  1071. it('validates args', function()
  1072. eq('Invalid lines (expected array of strings)',
  1073. pcall_err(request, 'nvim_put', {42}, 'l', false, false))
  1074. eq("Invalid type: 'x'",
  1075. pcall_err(request, 'nvim_put', {'foo'}, 'x', false, false))
  1076. end)
  1077. it("fails if 'nomodifiable'", function()
  1078. command('set nomodifiable')
  1079. eq([[Vim:E21: Cannot make changes, 'modifiable' is off]],
  1080. pcall_err(request, 'nvim_put', {'a','b'}, 'l', true, true))
  1081. end)
  1082. it('inserts text', function()
  1083. -- linewise
  1084. nvim('put', {'line 1','line 2','line 3'}, 'l', true, true)
  1085. expect([[
  1086. line 1
  1087. line 2
  1088. line 3]])
  1089. eq({0,4,1,0}, funcs.getpos('.'))
  1090. command('%delete _')
  1091. -- charwise
  1092. nvim('put', {'line 1','line 2','line 3'}, 'c', true, false)
  1093. expect([[
  1094. line 1
  1095. line 2
  1096. line 3]])
  1097. eq({0,1,1,0}, funcs.getpos('.')) -- follow=false
  1098. -- blockwise
  1099. nvim('put', {'AA','BB'}, 'b', true, true)
  1100. expect([[
  1101. lAAine 1
  1102. lBBine 2
  1103. line 3]])
  1104. eq({0,2,4,0}, funcs.getpos('.'))
  1105. command('%delete _')
  1106. -- Empty lines list.
  1107. nvim('put', {}, 'c', true, true)
  1108. eq({0,1,1,0}, funcs.getpos('.'))
  1109. expect([[]])
  1110. -- Single empty line.
  1111. nvim('put', {''}, 'c', true, true)
  1112. eq({0,1,1,0}, funcs.getpos('.'))
  1113. expect([[
  1114. ]])
  1115. nvim('put', {'AB'}, 'c', true, true)
  1116. -- after=false, follow=true
  1117. nvim('put', {'line 1','line 2'}, 'c', false, true)
  1118. expect([[
  1119. Aline 1
  1120. line 2B]])
  1121. eq({0,2,7,0}, funcs.getpos('.'))
  1122. command('%delete _')
  1123. nvim('put', {'AB'}, 'c', true, true)
  1124. -- after=false, follow=false
  1125. nvim('put', {'line 1','line 2'}, 'c', false, false)
  1126. expect([[
  1127. Aline 1
  1128. line 2B]])
  1129. eq({0,1,2,0}, funcs.getpos('.'))
  1130. eq('', nvim('eval', 'v:errmsg'))
  1131. end)
  1132. it('detects charwise/linewise text (empty {type})', function()
  1133. -- linewise (final item is empty string)
  1134. nvim('put', {'line 1','line 2','line 3',''}, '', true, true)
  1135. expect([[
  1136. line 1
  1137. line 2
  1138. line 3]])
  1139. eq({0,4,1,0}, funcs.getpos('.'))
  1140. command('%delete _')
  1141. -- charwise (final item is non-empty)
  1142. nvim('put', {'line 1','line 2','line 3'}, '', true, true)
  1143. expect([[
  1144. line 1
  1145. line 2
  1146. line 3]])
  1147. eq({0,3,6,0}, funcs.getpos('.'))
  1148. end)
  1149. it('allows block width', function()
  1150. -- behave consistently with setreg(); support "\022{NUM}" return by getregtype()
  1151. meths.put({'line 1','line 2','line 3'}, 'l', false, false)
  1152. expect([[
  1153. line 1
  1154. line 2
  1155. line 3
  1156. ]])
  1157. -- larger width create spaces
  1158. meths.put({'a', 'bc'}, 'b3', false, false)
  1159. expect([[
  1160. a line 1
  1161. bc line 2
  1162. line 3
  1163. ]])
  1164. -- smaller width is ignored
  1165. meths.put({'xxx', 'yyy'}, '\0221', false, true)
  1166. expect([[
  1167. xxxa line 1
  1168. yyybc line 2
  1169. line 3
  1170. ]])
  1171. eq("Invalid type: 'bx'",
  1172. pcall_err(meths.put, {'xxx', 'yyy'}, 'bx', false, true))
  1173. eq("Invalid type: 'b3x'",
  1174. pcall_err(meths.put, {'xxx', 'yyy'}, 'b3x', false, true))
  1175. end)
  1176. end)
  1177. describe('nvim_strwidth', function()
  1178. it('works', function()
  1179. eq(3, nvim('strwidth', 'abc'))
  1180. -- 6 + (neovim)
  1181. -- 19 * 2 (each japanese character occupies two cells)
  1182. eq(44, nvim('strwidth', 'neovimのデザインかなりまともなのになってる。'))
  1183. end)
  1184. it('cannot handle NULs', function()
  1185. eq(0, nvim('strwidth', '\0abc'))
  1186. end)
  1187. end)
  1188. describe('nvim_get_current_line, nvim_set_current_line', function()
  1189. it('works', function()
  1190. eq('', nvim('get_current_line'))
  1191. nvim('set_current_line', 'abc')
  1192. eq('abc', nvim('get_current_line'))
  1193. end)
  1194. end)
  1195. describe('set/get/del variables', function()
  1196. it('nvim_get_var, nvim_set_var, nvim_del_var', function()
  1197. nvim('set_var', 'lua', {1, 2, {['3'] = 1}})
  1198. eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua'))
  1199. eq({1, 2, {['3'] = 1}}, nvim('eval', 'g:lua'))
  1200. eq(1, funcs.exists('g:lua'))
  1201. meths.del_var('lua')
  1202. eq(0, funcs.exists('g:lua'))
  1203. eq("Key not found: lua", pcall_err(meths.del_var, 'lua'))
  1204. meths.set_var('lua', 1)
  1205. -- Set locked g: var.
  1206. command('lockvar lua')
  1207. eq('Key is locked: lua', pcall_err(meths.del_var, 'lua'))
  1208. eq('Key is locked: lua', pcall_err(meths.set_var, 'lua', 1))
  1209. exec([[
  1210. function Test()
  1211. endfunction
  1212. function s:Test()
  1213. endfunction
  1214. let g:Unknown_func = function('Test')
  1215. let g:Unknown_script_func = function('s:Test')
  1216. ]])
  1217. eq(NIL, meths.get_var('Unknown_func'))
  1218. eq(NIL, meths.get_var('Unknown_script_func'))
  1219. -- Check if autoload works properly
  1220. local pathsep = helpers.get_pathsep()
  1221. local xconfig = 'Xhome' .. pathsep .. 'Xconfig'
  1222. local xdata = 'Xhome' .. pathsep .. 'Xdata'
  1223. local autoload_folder = table.concat({xconfig, 'nvim', 'autoload'}, pathsep)
  1224. local autoload_file = table.concat({autoload_folder , 'testload.vim'}, pathsep)
  1225. mkdir_p(autoload_folder)
  1226. write_file(autoload_file , [[let testload#value = 2]])
  1227. clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata } }
  1228. eq(2, meths.get_var('testload#value'))
  1229. rmdir('Xhome')
  1230. end)
  1231. it('nvim_get_vvar, nvim_set_vvar', function()
  1232. -- Set readonly v: var.
  1233. eq('Key is read-only: count',
  1234. pcall_err(request, 'nvim_set_vvar', 'count', 42))
  1235. -- Set writable v: var.
  1236. meths.set_vvar('errmsg', 'set by API')
  1237. eq('set by API', meths.get_vvar('errmsg'))
  1238. end)
  1239. it('vim_set_var returns the old value', function()
  1240. local val1 = {1, 2, {['3'] = 1}}
  1241. local val2 = {4, 7}
  1242. eq(NIL, request('vim_set_var', 'lua', val1))
  1243. eq(val1, request('vim_set_var', 'lua', val2))
  1244. end)
  1245. it('vim_del_var returns the old value', function()
  1246. local val1 = {1, 2, {['3'] = 1}}
  1247. local val2 = {4, 7}
  1248. eq(NIL, request('vim_set_var', 'lua', val1))
  1249. eq(val1, request('vim_set_var', 'lua', val2))
  1250. eq(val2, request('vim_del_var', 'lua'))
  1251. end)
  1252. it('truncates values with NULs in them', function()
  1253. nvim('set_var', 'xxx', 'ab\0cd')
  1254. eq('ab', nvim('get_var', 'xxx'))
  1255. end)
  1256. end)
  1257. describe('nvim_get_option, nvim_set_option', function()
  1258. it('works', function()
  1259. ok(nvim('get_option', 'equalalways'))
  1260. nvim('set_option', 'equalalways', false)
  1261. ok(not nvim('get_option', 'equalalways'))
  1262. end)
  1263. it('works to get global value of local options', function()
  1264. eq(false, nvim('get_option', 'lisp'))
  1265. eq(8, nvim('get_option', 'shiftwidth'))
  1266. end)
  1267. it('works to set global value of local options', function()
  1268. nvim('set_option', 'lisp', true)
  1269. eq(true, nvim('get_option', 'lisp'))
  1270. eq(false, helpers.curbuf('get_option', 'lisp'))
  1271. eq(nil, nvim('command_output', 'setglobal lisp?'):match('nolisp'))
  1272. eq('nolisp', nvim('command_output', 'setlocal lisp?'):match('nolisp'))
  1273. nvim('set_option', 'shiftwidth', 20)
  1274. eq('20', nvim('command_output', 'setglobal shiftwidth?'):match('%d+'))
  1275. eq('8', nvim('command_output', 'setlocal shiftwidth?'):match('%d+'))
  1276. end)
  1277. it('most window-local options have no global value', function()
  1278. local status, err = pcall(nvim, 'get_option', 'foldcolumn')
  1279. eq(false, status)
  1280. ok(err:match('Invalid option name') ~= nil)
  1281. end)
  1282. it('updates where the option was last set from', function()
  1283. nvim('set_option', 'equalalways', false)
  1284. local status, rv = pcall(nvim, 'command_output',
  1285. 'verbose set equalalways?')
  1286. eq(true, status)
  1287. ok(nil ~= string.find(rv, 'noequalalways\n'..
  1288. '\tLast set from API client %(channel id %d+%)'))
  1289. nvim('exec_lua', 'vim.api.nvim_set_option("equalalways", true)', {})
  1290. status, rv = pcall(nvim, 'command_output',
  1291. 'verbose set equalalways?')
  1292. eq(true, status)
  1293. eq(' equalalways\n\tLast set from Lua', rv)
  1294. end)
  1295. end)
  1296. describe('nvim_get_option_value, nvim_set_option_value', function()
  1297. it('works', function()
  1298. ok(nvim('get_option_value', 'equalalways', {}))
  1299. nvim('set_option_value', 'equalalways', false, {})
  1300. ok(not nvim('get_option_value', 'equalalways', {}))
  1301. end)
  1302. it('can get local values when global value is set', function()
  1303. eq(0, nvim('get_option_value', 'scrolloff', {}))
  1304. eq(-1, nvim('get_option_value', 'scrolloff', {scope = 'local'}))
  1305. end)
  1306. it('can set global and local values', function()
  1307. nvim('set_option_value', 'makeprg', 'hello', {})
  1308. eq('hello', nvim('get_option_value', 'makeprg', {}))
  1309. eq('', nvim('get_option_value', 'makeprg', {scope = 'local'}))
  1310. nvim('set_option_value', 'makeprg', 'world', {scope = 'local'})
  1311. eq('world', nvim('get_option_value', 'makeprg', {scope = 'local'}))
  1312. nvim('set_option_value', 'makeprg', 'goodbye', {scope = 'global'})
  1313. eq('goodbye', nvim('get_option_value', 'makeprg', {scope = 'global'}))
  1314. nvim('set_option_value', 'makeprg', 'hello', {})
  1315. eq('hello', nvim('get_option_value', 'makeprg', {scope = 'global'}))
  1316. eq('hello', nvim('get_option_value', 'makeprg', {}))
  1317. eq('', nvim('get_option_value', 'makeprg', {scope = 'local'}))
  1318. end)
  1319. it('clears the local value of an option with nil', function()
  1320. -- Set global value
  1321. nvim('set_option_value', 'shiftwidth', 42, {})
  1322. eq(42, nvim('get_option_value', 'shiftwidth', {}))
  1323. -- Set local value
  1324. nvim('set_option_value', 'shiftwidth', 8, {scope = 'local'})
  1325. eq(8, nvim('get_option_value', 'shiftwidth', {}))
  1326. eq(8, nvim('get_option_value', 'shiftwidth', {scope = 'local'}))
  1327. eq(42, nvim('get_option_value', 'shiftwidth', {scope = 'global'}))
  1328. -- Clear value without scope
  1329. nvim('set_option_value', 'shiftwidth', NIL, {})
  1330. eq(42, nvim('get_option_value', 'shiftwidth', {}))
  1331. eq(42, nvim('get_option_value', 'shiftwidth', {scope = 'local'}))
  1332. -- Clear value with explicit scope
  1333. nvim('set_option_value', 'shiftwidth', 8, {scope = 'local'})
  1334. nvim('set_option_value', 'shiftwidth', NIL, {scope = 'local'})
  1335. eq(42, nvim('get_option_value', 'shiftwidth', {}))
  1336. eq(42, nvim('get_option_value', 'shiftwidth', {scope = 'local'}))
  1337. -- Now try with options with a special "local is unset" value (e.g. 'undolevels')
  1338. nvim('set_option_value', 'undolevels', 1000, {})
  1339. eq(1000, nvim('get_option_value', 'undolevels', {scope = 'local'}))
  1340. nvim('set_option_value', 'undolevels', NIL, {scope = 'local'})
  1341. eq(-123456, nvim('get_option_value', 'undolevels', {scope = 'local'}))
  1342. nvim('set_option_value', 'autoread', true, {})
  1343. eq(true, nvim('get_option_value', 'autoread', {scope = 'local'}))
  1344. nvim('set_option_value', 'autoread', NIL, {scope = 'local'})
  1345. eq(NIL, nvim('get_option_value', 'autoread', {scope = 'local'}))
  1346. end)
  1347. it('set window options', function()
  1348. -- Same as to nvim_win_set_option
  1349. nvim('set_option_value', 'colorcolumn', '4,3', {win=0})
  1350. eq('4,3', nvim('get_option_value', 'colorcolumn', {scope = 'local'}))
  1351. command("set modified hidden")
  1352. command("enew") -- edit new buffer, window option is preserved
  1353. eq('4,3', nvim('get_option_value', 'colorcolumn', {scope = 'local'}))
  1354. end)
  1355. it('set local window options', function()
  1356. -- Different to nvim_win_set_option
  1357. nvim('set_option_value', 'colorcolumn', '4,3', {win=0, scope='local'})
  1358. eq('4,3', nvim('get_option_value', 'colorcolumn', {win = 0, scope = 'local'}))
  1359. command("set modified hidden")
  1360. command("enew") -- edit new buffer, window option is reset
  1361. eq('', nvim('get_option_value', 'colorcolumn', {win = 0, scope = 'local'}))
  1362. end)
  1363. it('get buffer or window-local options', function()
  1364. nvim('command', 'new')
  1365. local buf = nvim('get_current_buf').id
  1366. nvim('buf_set_option', buf, 'tagfunc', 'foobar')
  1367. eq('foobar', nvim('get_option_value', 'tagfunc', {buf = buf}))
  1368. local win = nvim('get_current_win').id
  1369. nvim('win_set_option', win, 'number', true)
  1370. eq(true, nvim('get_option_value', 'number', {win = win}))
  1371. end)
  1372. it('getting current buffer option does not adjust cursor #19381', function()
  1373. nvim('command', 'new')
  1374. local buf = nvim('get_current_buf').id
  1375. local win = nvim('get_current_win').id
  1376. insert('some text')
  1377. feed('0v$')
  1378. eq({1, 9}, nvim('win_get_cursor', win))
  1379. nvim('get_option_value', 'filetype', {buf = buf})
  1380. eq({1, 9}, nvim('win_get_cursor', win))
  1381. end)
  1382. end)
  1383. describe('nvim_{get,set}_current_buf, nvim_list_bufs', function()
  1384. it('works', function()
  1385. eq(1, #nvim('list_bufs'))
  1386. eq(nvim('list_bufs')[1], nvim('get_current_buf'))
  1387. nvim('command', 'new')
  1388. eq(2, #nvim('list_bufs'))
  1389. eq(nvim('list_bufs')[2], nvim('get_current_buf'))
  1390. nvim('set_current_buf', nvim('list_bufs')[1])
  1391. eq(nvim('list_bufs')[1], nvim('get_current_buf'))
  1392. end)
  1393. end)
  1394. describe('nvim_{get,set}_current_win, nvim_list_wins', function()
  1395. it('works', function()
  1396. eq(1, #nvim('list_wins'))
  1397. eq(nvim('list_wins')[1], nvim('get_current_win'))
  1398. nvim('command', 'vsplit')
  1399. nvim('command', 'split')
  1400. eq(3, #nvim('list_wins'))
  1401. eq(nvim('list_wins')[1], nvim('get_current_win'))
  1402. nvim('set_current_win', nvim('list_wins')[2])
  1403. eq(nvim('list_wins')[2], nvim('get_current_win'))
  1404. end)
  1405. end)
  1406. describe('nvim_{get,set}_current_tabpage, nvim_list_tabpages', function()
  1407. it('works', function()
  1408. eq(1, #nvim('list_tabpages'))
  1409. eq(nvim('list_tabpages')[1], nvim('get_current_tabpage'))
  1410. nvim('command', 'tabnew')
  1411. eq(2, #nvim('list_tabpages'))
  1412. eq(2, #nvim('list_wins'))
  1413. eq(nvim('list_wins')[2], nvim('get_current_win'))
  1414. eq(nvim('list_tabpages')[2], nvim('get_current_tabpage'))
  1415. nvim('set_current_win', nvim('list_wins')[1])
  1416. -- Switching window also switches tabpages if necessary
  1417. eq(nvim('list_tabpages')[1], nvim('get_current_tabpage'))
  1418. eq(nvim('list_wins')[1], nvim('get_current_win'))
  1419. nvim('set_current_tabpage', nvim('list_tabpages')[2])
  1420. eq(nvim('list_tabpages')[2], nvim('get_current_tabpage'))
  1421. eq(nvim('list_wins')[2], nvim('get_current_win'))
  1422. end)
  1423. end)
  1424. describe('nvim_get_mode', function()
  1425. it("during normal-mode `g` returns blocking=true", function()
  1426. nvim("input", "o") -- add a line
  1427. eq({mode='i', blocking=false}, nvim("get_mode"))
  1428. nvim("input", [[<C-\><C-N>]])
  1429. eq(2, nvim("eval", "line('.')"))
  1430. eq({mode='n', blocking=false}, nvim("get_mode"))
  1431. nvim("input", "g")
  1432. eq({mode='n', blocking=true}, nvim("get_mode"))
  1433. nvim("input", "k") -- complete the operator
  1434. eq(1, nvim("eval", "line('.')")) -- verify the completed operator
  1435. eq({mode='n', blocking=false}, nvim("get_mode"))
  1436. end)
  1437. it("returns the correct result multiple consecutive times", function()
  1438. for _ = 1,5 do
  1439. eq({mode='n', blocking=false}, nvim("get_mode"))
  1440. end
  1441. nvim("input", "g")
  1442. for _ = 1,4 do
  1443. eq({mode='n', blocking=true}, nvim("get_mode"))
  1444. end
  1445. nvim("input", "g")
  1446. for _ = 1,7 do
  1447. eq({mode='n', blocking=false}, nvim("get_mode"))
  1448. end
  1449. end)
  1450. it("during normal-mode CTRL-W, returns blocking=true", function()
  1451. nvim("input", "<C-W>")
  1452. eq({mode='n', blocking=true}, nvim("get_mode"))
  1453. nvim("input", "s") -- complete the operator
  1454. eq(2, nvim("eval", "winnr('$')")) -- verify the completed operator
  1455. eq({mode='n', blocking=false}, nvim("get_mode"))
  1456. end)
  1457. it("during press-enter prompt without UI returns blocking=false", function()
  1458. eq({mode='n', blocking=false}, nvim("get_mode"))
  1459. command("echom 'msg1'")
  1460. command("echom 'msg2'")
  1461. command("echom 'msg3'")
  1462. command("echom 'msg4'")
  1463. command("echom 'msg5'")
  1464. eq({mode='n', blocking=false}, nvim("get_mode"))
  1465. nvim("input", ":messages<CR>")
  1466. eq({mode='n', blocking=false}, nvim("get_mode"))
  1467. end)
  1468. it("during press-enter prompt returns blocking=true", function()
  1469. nvim("ui_attach", 80, 20, {})
  1470. eq({mode='n', blocking=false}, nvim("get_mode"))
  1471. command("echom 'msg1'")
  1472. command("echom 'msg2'")
  1473. command("echom 'msg3'")
  1474. command("echom 'msg4'")
  1475. command("echom 'msg5'")
  1476. eq({mode='n', blocking=false}, nvim("get_mode"))
  1477. nvim("input", ":messages<CR>")
  1478. eq({mode='r', blocking=true}, nvim("get_mode"))
  1479. end)
  1480. it("during getchar() returns blocking=false", function()
  1481. nvim("input", ":let g:test_input = nr2char(getchar())<CR>")
  1482. -- Events are enabled during getchar(), RPC calls are *not* blocked. #5384
  1483. eq({mode='n', blocking=false}, nvim("get_mode"))
  1484. eq(0, nvim("eval", "exists('g:test_input')"))
  1485. nvim("input", "J")
  1486. eq("J", nvim("eval", "g:test_input"))
  1487. eq({mode='n', blocking=false}, nvim("get_mode"))
  1488. end)
  1489. -- TODO: bug #6247#issuecomment-286403810
  1490. it("batched with input", function()
  1491. nvim("ui_attach", 80, 20, {})
  1492. eq({mode='n', blocking=false}, nvim("get_mode"))
  1493. command("echom 'msg1'")
  1494. command("echom 'msg2'")
  1495. command("echom 'msg3'")
  1496. command("echom 'msg4'")
  1497. command("echom 'msg5'")
  1498. local req = {
  1499. {'nvim_get_mode', {}},
  1500. {'nvim_input', {':messages<CR>'}},
  1501. {'nvim_get_mode', {}},
  1502. {'nvim_eval', {'1'}},
  1503. }
  1504. eq({ { {mode='n', blocking=false},
  1505. 13,
  1506. {mode='n', blocking=false}, -- TODO: should be blocked=true ?
  1507. 1 },
  1508. NIL}, meths.call_atomic(req))
  1509. eq({mode='r', blocking=true}, nvim("get_mode"))
  1510. end)
  1511. it("during insert-mode map-pending, returns blocking=true #6166", function()
  1512. command("inoremap xx foo")
  1513. nvim("input", "ix")
  1514. eq({mode='i', blocking=true}, nvim("get_mode"))
  1515. end)
  1516. it("during normal-mode gU, returns blocking=false #6166", function()
  1517. nvim("input", "gu")
  1518. eq({mode='no', blocking=false}, nvim("get_mode"))
  1519. end)
  1520. it("at '-- More --' prompt returns blocking=true #11899", function()
  1521. command('set more')
  1522. feed(':digraphs<cr>')
  1523. eq({mode='rm', blocking=true}, nvim("get_mode"))
  1524. end)
  1525. it('after <Nop> mapping returns blocking=false #17257', function()
  1526. command('nnoremap <F2> <Nop>')
  1527. feed('<F2>')
  1528. eq({mode='n', blocking=false}, nvim("get_mode"))
  1529. end)
  1530. it('after empty string <expr> mapping returns blocking=false #17257', function()
  1531. command('nnoremap <expr> <F2> ""')
  1532. feed('<F2>')
  1533. eq({mode='n', blocking=false}, nvim("get_mode"))
  1534. end)
  1535. end)
  1536. describe('RPC (K_EVENT)', function()
  1537. it('does not complete ("interrupt") normal-mode operator-pending #6166', function()
  1538. helpers.insert([[
  1539. FIRST LINE
  1540. SECOND LINE]])
  1541. nvim('input', 'gg')
  1542. nvim('input', 'gu')
  1543. -- Make any RPC request (can be non-async: op-pending does not block).
  1544. nvim('get_current_buf')
  1545. -- Buffer should not change.
  1546. expect([[
  1547. FIRST LINE
  1548. SECOND LINE]])
  1549. -- Now send input to complete the operator.
  1550. nvim('input', 'j')
  1551. expect([[
  1552. first line
  1553. second line]])
  1554. end)
  1555. it('does not complete ("interrupt") `d` #3732', function()
  1556. local screen = Screen.new(20, 4)
  1557. screen:attach()
  1558. command('set listchars=eol:$')
  1559. command('set list')
  1560. feed('ia<cr>b<cr>c<cr><Esc>kkk')
  1561. feed('d')
  1562. -- Make any RPC request (can be non-async: op-pending does not block).
  1563. nvim('get_current_buf')
  1564. screen:expect([[
  1565. ^a$ |
  1566. b$ |
  1567. c$ |
  1568. |
  1569. ]])
  1570. end)
  1571. it('does not complete ("interrupt") normal-mode map-pending #6166', function()
  1572. command("nnoremap dd :let g:foo='it worked...'<CR>")
  1573. helpers.insert([[
  1574. FIRST LINE
  1575. SECOND LINE]])
  1576. nvim('input', 'gg')
  1577. nvim('input', 'd')
  1578. -- Make any RPC request (must be async, because map-pending blocks).
  1579. nvim('get_api_info')
  1580. -- Send input to complete the mapping.
  1581. nvim('input', 'd')
  1582. expect([[
  1583. FIRST LINE
  1584. SECOND LINE]])
  1585. eq('it worked...', helpers.eval('g:foo'))
  1586. end)
  1587. it('does not complete ("interrupt") insert-mode map-pending #6166', function()
  1588. command('inoremap xx foo')
  1589. command('set timeoutlen=9999')
  1590. helpers.insert([[
  1591. FIRST LINE
  1592. SECOND LINE]])
  1593. nvim('input', 'ix')
  1594. -- Make any RPC request (must be async, because map-pending blocks).
  1595. nvim('get_api_info')
  1596. -- Send input to complete the mapping.
  1597. nvim('input', 'x')
  1598. expect([[
  1599. FIRST LINE
  1600. SECOND LINfooE]])
  1601. end)
  1602. it('does not interrupt Insert mode i_CTRL-O #10035', function()
  1603. feed('iHello World<c-o>')
  1604. eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event
  1605. eq(2, eval('1+1')) -- causes K_EVENT key
  1606. eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode
  1607. feed('dd')
  1608. eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode
  1609. expect('') -- executed the command
  1610. end)
  1611. it('does not interrupt Select mode v_CTRL-O #15688', function()
  1612. feed('iHello World<esc>gh<c-o>')
  1613. eq({mode='vs', blocking=false}, meths.get_mode()) -- fast event
  1614. eq({mode='vs', blocking=false}, meths.get_mode()) -- again #15288
  1615. eq(2, eval('1+1')) -- causes K_EVENT key
  1616. eq({mode='vs', blocking=false}, meths.get_mode()) -- still in ctrl-o mode
  1617. feed('^')
  1618. eq({mode='s', blocking=false}, meths.get_mode()) -- left ctrl-o mode
  1619. feed('h')
  1620. eq({mode='i', blocking=false}, meths.get_mode()) -- entered insert mode
  1621. expect('h') -- selection is the whole line and is replaced
  1622. end)
  1623. it('does not interrupt Insert mode i_0_CTRL-D #13997', function()
  1624. command('set timeoutlen=9999')
  1625. feed('i<Tab><Tab>a0')
  1626. eq(2, eval('1+1')) -- causes K_EVENT key
  1627. feed('<C-D>')
  1628. expect('a') -- recognized i_0_CTRL-D
  1629. end)
  1630. end)
  1631. describe('nvim_get_context', function()
  1632. it('validates args', function()
  1633. eq("Invalid key: 'blah'",
  1634. pcall_err(nvim, 'get_context', {blah={}}))
  1635. eq('invalid value for key: types',
  1636. pcall_err(nvim, 'get_context', {types=42}))
  1637. eq('unexpected type: zub',
  1638. pcall_err(nvim, 'get_context', {types={'jumps', 'zub', 'zam',}}))
  1639. end)
  1640. it('returns map of current editor state', function()
  1641. local opts = {types={'regs', 'jumps', 'bufs', 'gvars'}}
  1642. eq({}, parse_context(nvim('get_context', {})))
  1643. feed('i1<cr>2<cr>3<c-[>ddddddqahjklquuu')
  1644. feed('gg')
  1645. feed('G')
  1646. command('edit! BUF1')
  1647. command('edit BUF2')
  1648. nvim('set_var', 'one', 1)
  1649. nvim('set_var', 'Two', 2)
  1650. nvim('set_var', 'THREE', 3)
  1651. local expected_ctx = {
  1652. ['regs'] = {
  1653. {['rt'] = 1, ['rc'] = {'1'}, ['n'] = 49, ['ru'] = true},
  1654. {['rt'] = 1, ['rc'] = {'2'}, ['n'] = 50},
  1655. {['rt'] = 1, ['rc'] = {'3'}, ['n'] = 51},
  1656. {['rc'] = {'hjkl'}, ['n'] = 97},
  1657. },
  1658. ['jumps'] = eval(([[
  1659. filter(map(add(
  1660. getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }),
  1661. 'filter(
  1662. { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
  1663. { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
  1664. ]]):gsub('\n', '')),
  1665. ['bufs'] = eval([[
  1666. filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)')
  1667. ]]),
  1668. ['gvars'] = {{'one', 1}, {'Two', 2}, {'THREE', 3}},
  1669. }
  1670. eq(expected_ctx, parse_context(nvim('get_context', opts)))
  1671. eq(expected_ctx, parse_context(nvim('get_context', {})))
  1672. eq(expected_ctx, parse_context(nvim('get_context', {types={}})))
  1673. end)
  1674. end)
  1675. describe('nvim_load_context', function()
  1676. it('sets current editor state to given context dictionary', function()
  1677. local opts = {types={'regs', 'jumps', 'bufs', 'gvars'}}
  1678. eq({}, parse_context(nvim('get_context', opts)))
  1679. nvim('set_var', 'one', 1)
  1680. nvim('set_var', 'Two', 2)
  1681. nvim('set_var', 'THREE', 3)
  1682. local ctx = nvim('get_context', opts)
  1683. nvim('set_var', 'one', 'a')
  1684. nvim('set_var', 'Two', 'b')
  1685. nvim('set_var', 'THREE', 'c')
  1686. eq({'a', 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]'))
  1687. nvim('load_context', ctx)
  1688. eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]'))
  1689. end)
  1690. end)
  1691. describe('nvim_replace_termcodes', function()
  1692. it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function()
  1693. eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true))
  1694. end)
  1695. it('leaves non-K_SPECIAL string unchanged', function()
  1696. eq('abc', helpers.nvim('replace_termcodes', 'abc', true, true, true))
  1697. end)
  1698. it('converts <expressions>', function()
  1699. eq('\\', helpers.nvim('replace_termcodes', '<Leader>', true, true, true))
  1700. end)
  1701. it('converts <LeftMouse> to K_SPECIAL KS_EXTRA KE_LEFTMOUSE', function()
  1702. -- K_SPECIAL KS_EXTRA KE_LEFTMOUSE
  1703. -- 0x80 0xfd 0x2c
  1704. -- 128 253 44
  1705. eq('\128\253\44', helpers.nvim('replace_termcodes',
  1706. '<LeftMouse>', true, true, true))
  1707. end)
  1708. it('converts keycodes', function()
  1709. eq('\nx\27x\rx<x', helpers.nvim('replace_termcodes',
  1710. '<NL>x<Esc>x<CR>x<lt>x', true, true, true))
  1711. end)
  1712. it('does not convert keycodes if special=false', function()
  1713. eq('<NL>x<Esc>x<CR>x<lt>x', helpers.nvim('replace_termcodes',
  1714. '<NL>x<Esc>x<CR>x<lt>x', true, true, false))
  1715. end)
  1716. it('does not crash when transforming an empty string', function()
  1717. -- Actually does not test anything, because current code will use NULL for
  1718. -- an empty string.
  1719. --
  1720. -- Problem here is that if String argument has .data in allocated memory
  1721. -- then `return str` in vim_replace_termcodes body will make Neovim free
  1722. -- `str.data` twice: once when freeing arguments, then when freeing return
  1723. -- value.
  1724. eq('', meths.replace_termcodes('', true, true, true))
  1725. end)
  1726. end)
  1727. describe('nvim_feedkeys', function()
  1728. it('K_SPECIAL escaping', function()
  1729. local function on_setup()
  1730. -- notice the special char(…) \xe2\80\xa6
  1731. nvim('feedkeys', ':let x1="…"\n', '', true)
  1732. -- Both nvim_replace_termcodes and nvim_feedkeys escape \x80
  1733. local inp = helpers.nvim('replace_termcodes', ':let x2="…"<CR>', true, true, true)
  1734. nvim('feedkeys', inp, '', true) -- escape_ks=true
  1735. -- nvim_feedkeys with K_SPECIAL escaping disabled
  1736. inp = helpers.nvim('replace_termcodes', ':let x3="…"<CR>', true, true, true)
  1737. nvim('feedkeys', inp, '', false) -- escape_ks=false
  1738. helpers.stop()
  1739. end
  1740. -- spin the loop a bit
  1741. helpers.run(nil, nil, on_setup)
  1742. eq('…', nvim('get_var', 'x1'))
  1743. -- Because of the double escaping this is neq
  1744. neq('…', nvim('get_var', 'x2'))
  1745. eq('…', nvim('get_var', 'x3'))
  1746. end)
  1747. end)
  1748. describe('nvim_err_write', function()
  1749. local screen
  1750. before_each(function()
  1751. clear()
  1752. screen = Screen.new(40, 8)
  1753. screen:attach()
  1754. screen:set_default_attr_ids({
  1755. [0] = {bold=true, foreground=Screen.colors.Blue},
  1756. [1] = {foreground = Screen.colors.White, background = Screen.colors.Red},
  1757. [2] = {bold = true, foreground = Screen.colors.SeaGreen},
  1758. [3] = {bold = true, reverse = true},
  1759. })
  1760. end)
  1761. it('can show one line', function()
  1762. nvim_async('err_write', 'has bork\n')
  1763. screen:expect([[
  1764. ^ |
  1765. {0:~ }|
  1766. {0:~ }|
  1767. {0:~ }|
  1768. {0:~ }|
  1769. {0:~ }|
  1770. {0:~ }|
  1771. {1:has bork} |
  1772. ]])
  1773. end)
  1774. it('shows return prompt when more than &cmdheight lines', function()
  1775. nvim_async('err_write', 'something happened\nvery bad\n')
  1776. screen:expect([[
  1777. |
  1778. {0:~ }|
  1779. {0:~ }|
  1780. {0:~ }|
  1781. {3: }|
  1782. {1:something happened} |
  1783. {1:very bad} |
  1784. {2:Press ENTER or type command to continue}^ |
  1785. ]])
  1786. end)
  1787. it('shows return prompt after all lines are shown', function()
  1788. nvim_async('err_write', 'FAILURE\nERROR\nEXCEPTION\nTRACEBACK\n')
  1789. screen:expect([[
  1790. |
  1791. {0:~ }|
  1792. {3: }|
  1793. {1:FAILURE} |
  1794. {1:ERROR} |
  1795. {1:EXCEPTION} |
  1796. {1:TRACEBACK} |
  1797. {2:Press ENTER or type command to continue}^ |
  1798. ]])
  1799. end)
  1800. it('handles multiple calls', function()
  1801. -- without linebreak text is joined to one line
  1802. nvim_async('err_write', 'very ')
  1803. nvim_async('err_write', 'fail\n')
  1804. screen:expect([[
  1805. ^ |
  1806. {0:~ }|
  1807. {0:~ }|
  1808. {0:~ }|
  1809. {0:~ }|
  1810. {0:~ }|
  1811. {0:~ }|
  1812. {1:very fail} |
  1813. ]])
  1814. helpers.poke_eventloop()
  1815. -- shows up to &cmdheight lines
  1816. nvim_async('err_write', 'more fail\ntoo fail\n')
  1817. screen:expect([[
  1818. |
  1819. {0:~ }|
  1820. {0:~ }|
  1821. {0:~ }|
  1822. {3: }|
  1823. {1:more fail} |
  1824. {1:too fail} |
  1825. {2:Press ENTER or type command to continue}^ |
  1826. ]])
  1827. feed('<cr>') -- exit the press ENTER screen
  1828. end)
  1829. end)
  1830. describe('nvim_list_chans, nvim_get_chan_info', function()
  1831. before_each(function()
  1832. command('autocmd ChanOpen * let g:opened_event = deepcopy(v:event)')
  1833. command('autocmd ChanInfo * let g:info_event = deepcopy(v:event)')
  1834. end)
  1835. local testinfo = {
  1836. stream = 'stdio',
  1837. id = 1,
  1838. mode = 'rpc',
  1839. client = {},
  1840. }
  1841. local stderr = {
  1842. stream = 'stderr',
  1843. id = 2,
  1844. mode = 'bytes',
  1845. }
  1846. it('returns {} for invalid channel', function()
  1847. eq({}, meths.get_chan_info(0))
  1848. eq({}, meths.get_chan_info(-1))
  1849. -- more preallocated numbers might be added, try something high
  1850. eq({}, meths.get_chan_info(10))
  1851. end)
  1852. it('stream=stdio channel', function()
  1853. eq({[1]=testinfo,[2]=stderr}, meths.list_chans())
  1854. eq(testinfo, meths.get_chan_info(1))
  1855. eq(stderr, meths.get_chan_info(2))
  1856. meths.set_client_info("functionaltests",
  1857. {major=0, minor=3, patch=17},
  1858. 'ui',
  1859. {do_stuff={n_args={2,3}}},
  1860. {license= 'Apache2'})
  1861. local info = {
  1862. stream = 'stdio',
  1863. id = 1,
  1864. mode = 'rpc',
  1865. client = {
  1866. name='functionaltests',
  1867. version={major=0, minor=3, patch=17},
  1868. type='ui',
  1869. methods={do_stuff={n_args={2,3}}},
  1870. attributes={license='Apache2'},
  1871. },
  1872. }
  1873. eq({info=info}, meths.get_var("info_event"))
  1874. eq({[1]=info, [2]=stderr}, meths.list_chans())
  1875. eq(info, meths.get_chan_info(1))
  1876. end)
  1877. it('stream=job channel', function()
  1878. eq(3, eval("jobstart(['cat'], {'rpc': v:true})"))
  1879. local catpath = eval('exepath("cat")')
  1880. local info = {
  1881. stream='job',
  1882. id=3,
  1883. argv={ catpath },
  1884. mode='rpc',
  1885. client={},
  1886. }
  1887. eq({info=info}, meths.get_var("opened_event"))
  1888. eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans())
  1889. eq(info, meths.get_chan_info(3))
  1890. eval('rpcrequest(3, "nvim_set_client_info", "amazing-cat", {}, "remote",'..
  1891. '{"nvim_command":{"n_args":1}},'.. -- and so on
  1892. '{"description":"The Amazing Cat"})')
  1893. info = {
  1894. stream='job',
  1895. id=3,
  1896. argv={ catpath },
  1897. mode='rpc',
  1898. client = {
  1899. name='amazing-cat',
  1900. version={major=0},
  1901. type='remote',
  1902. methods={nvim_command={n_args=1}},
  1903. attributes={description="The Amazing Cat"},
  1904. },
  1905. }
  1906. eq({info=info}, meths.get_var("info_event"))
  1907. eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans())
  1908. eq("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",
  1909. pcall_err(eval, 'rpcrequest(3, "nvim_set_current_buf", -1)'))
  1910. end)
  1911. it('stream=job :terminal channel', function()
  1912. command(':terminal')
  1913. eq({id=1}, meths.get_current_buf())
  1914. eq(3, meths.buf_get_option(1, 'channel'))
  1915. local info = {
  1916. stream='job',
  1917. id=3,
  1918. argv={ eval('exepath(&shell)') },
  1919. mode='terminal',
  1920. buffer = 1,
  1921. pty='?',
  1922. }
  1923. local event = meths.get_var("opened_event")
  1924. if not iswin() then
  1925. info.pty = event.info.pty
  1926. neq(nil, string.match(info.pty, "^/dev/"))
  1927. end
  1928. eq({info=info}, event)
  1929. info.buffer = {id=1}
  1930. eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans())
  1931. eq(info, meths.get_chan_info(3))
  1932. -- :terminal with args + running process.
  1933. command(':exe "terminal" shellescape(v:progpath) "-u NONE -i NONE"')
  1934. eq(-1, eval('jobwait([&channel], 0)[0]')) -- Running?
  1935. local expected2 = {
  1936. stream = 'job',
  1937. id = 4,
  1938. argv = (
  1939. iswin() and {
  1940. eval('&shell'),
  1941. '/s',
  1942. '/c',
  1943. fmt('"%s -u NONE -i NONE"', eval('shellescape(v:progpath)')),
  1944. } or {
  1945. eval('&shell'),
  1946. eval('&shellcmdflag'),
  1947. fmt('%s -u NONE -i NONE', eval('shellescape(v:progpath)')),
  1948. }
  1949. ),
  1950. mode = 'terminal',
  1951. buffer = 2,
  1952. pty = '?',
  1953. }
  1954. local actual2 = eval('nvim_get_chan_info(&channel)')
  1955. expected2.pty = actual2.pty
  1956. eq(expected2, actual2)
  1957. -- :terminal with args + stopped process.
  1958. eq(1, eval('jobstop(&channel)'))
  1959. eval('jobwait([&channel], 1000)') -- Wait.
  1960. expected2.pty = (iswin() and '?' or '') -- pty stream was closed.
  1961. eq(expected2, eval('nvim_get_chan_info(&channel)'))
  1962. end)
  1963. end)
  1964. describe('nvim_call_atomic', function()
  1965. it('works', function()
  1966. meths.buf_set_lines(0, 0, -1, true, {'first'})
  1967. local req = {
  1968. {'nvim_get_current_line', {}},
  1969. {'nvim_set_current_line', {'second'}},
  1970. }
  1971. eq({{'first', NIL}, NIL}, meths.call_atomic(req))
  1972. eq({'second'}, meths.buf_get_lines(0, 0, -1, true))
  1973. end)
  1974. it('allows multiple return values', function()
  1975. local req = {
  1976. {'nvim_set_var', {'avar', true}},
  1977. {'nvim_set_var', {'bvar', 'string'}},
  1978. {'nvim_get_var', {'avar'}},
  1979. {'nvim_get_var', {'bvar'}},
  1980. }
  1981. eq({{NIL, NIL, true, 'string'}, NIL}, meths.call_atomic(req))
  1982. end)
  1983. it('is aborted by errors in call', function()
  1984. local error_types = meths.get_api_info()[2].error_types
  1985. local req = {
  1986. {'nvim_set_var', {'one', 1}},
  1987. {'nvim_buf_set_lines', {}},
  1988. {'nvim_set_var', {'two', 2}},
  1989. }
  1990. eq({{NIL}, {1, error_types.Exception.id,
  1991. 'Wrong number of arguments: expecting 5 but got 0'}},
  1992. meths.call_atomic(req))
  1993. eq(1, meths.get_var('one'))
  1994. eq(false, pcall(meths.get_var, 'two'))
  1995. -- still returns all previous successful calls
  1996. req = {
  1997. {'nvim_set_var', {'avar', 5}},
  1998. {'nvim_set_var', {'bvar', 'string'}},
  1999. {'nvim_get_var', {'avar'}},
  2000. {'nvim_buf_get_lines', {0, 10, 20, true}},
  2001. {'nvim_get_var', {'bvar'}},
  2002. }
  2003. eq({{NIL, NIL, 5}, {3, error_types.Validation.id, 'Index out of bounds'}},
  2004. meths.call_atomic(req))
  2005. req = {
  2006. {'i_am_not_a_method', {'xx'}},
  2007. {'nvim_set_var', {'avar', 10}},
  2008. }
  2009. eq({{}, {0, error_types.Exception.id, 'Invalid method: i_am_not_a_method'}},
  2010. meths.call_atomic(req))
  2011. eq(5, meths.get_var('avar'))
  2012. end)
  2013. it('throws error on malformed arguments', function()
  2014. local req = {
  2015. {'nvim_set_var', {'avar', 1}},
  2016. {'nvim_set_var'},
  2017. {'nvim_set_var', {'avar', 2}},
  2018. }
  2019. local status, err = pcall(meths.call_atomic, req)
  2020. eq(false, status)
  2021. ok(err:match('Items in calls array must be arrays of size 2') ~= nil)
  2022. -- call before was done, but not after
  2023. eq(1, meths.get_var('avar'))
  2024. req = {
  2025. { 'nvim_set_var', { 'bvar', { 2, 3 } } },
  2026. 12,
  2027. }
  2028. status, err = pcall(meths.call_atomic, req)
  2029. eq(false, status)
  2030. ok(err:match('Items in calls array must be arrays') ~= nil)
  2031. eq({2,3}, meths.get_var('bvar'))
  2032. req = {
  2033. {'nvim_set_current_line', 'little line'},
  2034. {'nvim_set_var', {'avar', 3}},
  2035. }
  2036. status, err = pcall(meths.call_atomic, req)
  2037. eq(false, status)
  2038. ok(err:match('Args must be Array') ~= nil)
  2039. -- call before was done, but not after
  2040. eq(1, meths.get_var('avar'))
  2041. eq({''}, meths.buf_get_lines(0, 0, -1, true))
  2042. end)
  2043. end)
  2044. describe('nvim_list_runtime_paths', function()
  2045. setup(function()
  2046. local pathsep = helpers.get_pathsep()
  2047. mkdir_p('Xtest'..pathsep..'a')
  2048. mkdir_p('Xtest'..pathsep..'b')
  2049. end)
  2050. teardown(function()
  2051. rmdir 'Xtest'
  2052. end)
  2053. before_each(function()
  2054. meths.set_current_dir 'Xtest'
  2055. end)
  2056. it('returns nothing with empty &runtimepath', function()
  2057. meths.set_option('runtimepath', '')
  2058. eq({}, meths.list_runtime_paths())
  2059. end)
  2060. it('returns single runtimepath', function()
  2061. meths.set_option('runtimepath', 'a')
  2062. eq({'a'}, meths.list_runtime_paths())
  2063. end)
  2064. it('returns two runtimepaths', function()
  2065. meths.set_option('runtimepath', 'a,b')
  2066. eq({'a', 'b'}, meths.list_runtime_paths())
  2067. end)
  2068. it('returns empty strings when appropriate', function()
  2069. meths.set_option('runtimepath', 'a,,b')
  2070. eq({'a', '', 'b'}, meths.list_runtime_paths())
  2071. meths.set_option('runtimepath', ',a,b')
  2072. eq({'', 'a', 'b'}, meths.list_runtime_paths())
  2073. -- Trailing "," is ignored. Use ",," if you really really want CWD.
  2074. meths.set_option('runtimepath', 'a,b,')
  2075. eq({'a', 'b'}, meths.list_runtime_paths())
  2076. meths.set_option('runtimepath', 'a,b,,')
  2077. eq({'a', 'b', ''}, meths.list_runtime_paths())
  2078. end)
  2079. it('truncates too long paths', function()
  2080. local long_path = ('/a'):rep(8192)
  2081. meths.set_option('runtimepath', long_path)
  2082. local paths_list = meths.list_runtime_paths()
  2083. eq({}, paths_list)
  2084. end)
  2085. end)
  2086. it('can throw exceptions', function()
  2087. local status, err = pcall(nvim, 'get_option', 'invalid-option')
  2088. eq(false, status)
  2089. ok(err:match('Invalid option name') ~= nil)
  2090. end)
  2091. it('does not truncate error message <1 MB #5984', function()
  2092. local very_long_name = 'A'..('x'):rep(10000)..'Z'
  2093. local status, err = pcall(nvim, 'get_option', very_long_name)
  2094. eq(false, status)
  2095. eq(very_long_name, err:match('Ax+Z?'))
  2096. end)
  2097. it("does not leak memory on incorrect argument types", function()
  2098. local status, err = pcall(nvim, 'set_current_dir',{'not', 'a', 'dir'})
  2099. eq(false, status)
  2100. ok(err:match(': Wrong type for argument 1 when calling nvim_set_current_dir, expecting String') ~= nil)
  2101. end)
  2102. describe('nvim_parse_expression', function()
  2103. before_each(function()
  2104. meths.set_option('isident', '')
  2105. end)
  2106. local it_maybe_pending = it
  2107. if helpers.isCI() and os.getenv('CONFIGURATION') == 'MSVC_32' then
  2108. -- For "works with &opt" (flaky on MSVC_32), but not easy to skip alone. #10241
  2109. it_maybe_pending = pending
  2110. end
  2111. local function simplify_east_api_node(line, east_api_node)
  2112. if east_api_node == NIL then
  2113. return nil
  2114. end
  2115. if east_api_node.children then
  2116. for k, v in pairs(east_api_node.children) do
  2117. east_api_node.children[k] = simplify_east_api_node(line, v)
  2118. end
  2119. end
  2120. local typ = east_api_node.type
  2121. if typ == 'Register' then
  2122. typ = typ .. ('(name=%s)'):format(
  2123. tostring(intchar2lua(east_api_node.name)))
  2124. east_api_node.name = nil
  2125. elseif typ == 'PlainIdentifier' then
  2126. typ = typ .. ('(scope=%s,ident=%s)'):format(
  2127. tostring(intchar2lua(east_api_node.scope)), east_api_node.ident)
  2128. east_api_node.scope = nil
  2129. east_api_node.ident = nil
  2130. elseif typ == 'PlainKey' then
  2131. typ = typ .. ('(key=%s)'):format(east_api_node.ident)
  2132. east_api_node.ident = nil
  2133. elseif typ == 'Comparison' then
  2134. typ = typ .. ('(type=%s,inv=%u,ccs=%s)'):format(
  2135. east_api_node.cmp_type, east_api_node.invert and 1 or 0,
  2136. east_api_node.ccs_strategy)
  2137. east_api_node.ccs_strategy = nil
  2138. east_api_node.cmp_type = nil
  2139. east_api_node.invert = nil
  2140. elseif typ == 'Integer' then
  2141. typ = typ .. ('(val=%u)'):format(east_api_node.ivalue)
  2142. east_api_node.ivalue = nil
  2143. elseif typ == 'Float' then
  2144. typ = typ .. format_string('(val=%e)', east_api_node.fvalue)
  2145. east_api_node.fvalue = nil
  2146. elseif typ == 'SingleQuotedString' or typ == 'DoubleQuotedString' then
  2147. typ = format_string('%s(val=%q)', typ, east_api_node.svalue)
  2148. east_api_node.svalue = nil
  2149. elseif typ == 'Option' then
  2150. typ = ('%s(scope=%s,ident=%s)'):format(
  2151. typ,
  2152. tostring(intchar2lua(east_api_node.scope)),
  2153. east_api_node.ident)
  2154. east_api_node.ident = nil
  2155. east_api_node.scope = nil
  2156. elseif typ == 'Environment' then
  2157. typ = ('%s(ident=%s)'):format(typ, east_api_node.ident)
  2158. east_api_node.ident = nil
  2159. elseif typ == 'Assignment' then
  2160. local aug = east_api_node.augmentation
  2161. if aug == '' then aug = 'Plain' end
  2162. typ = ('%s(%s)'):format(typ, aug)
  2163. east_api_node.augmentation = nil
  2164. end
  2165. typ = ('%s:%u:%u:%s'):format(
  2166. typ, east_api_node.start[1], east_api_node.start[2],
  2167. line:sub(east_api_node.start[2] + 1,
  2168. east_api_node.start[2] + 1 + east_api_node.len - 1))
  2169. assert(east_api_node.start[2] + east_api_node.len - 1 <= #line)
  2170. for k, _ in pairs(east_api_node.start) do
  2171. assert(({true, true})[k])
  2172. end
  2173. east_api_node.start = nil
  2174. east_api_node.type = nil
  2175. east_api_node.len = nil
  2176. local can_simplify = true
  2177. for _, _ in pairs(east_api_node) do
  2178. if can_simplify then can_simplify = false end
  2179. end
  2180. if can_simplify then
  2181. return typ
  2182. else
  2183. east_api_node[1] = typ
  2184. return east_api_node
  2185. end
  2186. end
  2187. local function simplify_east_api(line, east_api)
  2188. if east_api.error then
  2189. east_api.err = east_api.error
  2190. east_api.error = nil
  2191. east_api.err.msg = east_api.err.message
  2192. east_api.err.message = nil
  2193. end
  2194. if east_api.ast then
  2195. east_api.ast = {simplify_east_api_node(line, east_api.ast)}
  2196. if #east_api.ast == 0 then
  2197. east_api.ast = nil
  2198. end
  2199. end
  2200. if east_api.len == #line then
  2201. east_api.len = nil
  2202. end
  2203. return east_api
  2204. end
  2205. local function simplify_east_hl(line, east_hl)
  2206. for i, v in ipairs(east_hl) do
  2207. east_hl[i] = ('%s:%u:%u:%s'):format(
  2208. v[4],
  2209. v[1],
  2210. v[2],
  2211. line:sub(v[2] + 1, v[3]))
  2212. end
  2213. return east_hl
  2214. end
  2215. local FLAGS_TO_STR = {
  2216. [0] = "",
  2217. [1] = "m",
  2218. [2] = "E",
  2219. [3] = "mE",
  2220. [4] = "l",
  2221. [5] = "lm",
  2222. [6] = "lE",
  2223. [7] = "lmE",
  2224. }
  2225. local function _check_parsing(opts, str, exp_ast, exp_highlighting_fs,
  2226. nz_flags_exps)
  2227. if type(str) ~= 'string' then
  2228. return
  2229. end
  2230. local zflags = opts.flags[1]
  2231. nz_flags_exps = nz_flags_exps or {}
  2232. for _, flags in ipairs(opts.flags) do
  2233. local err, msg = pcall(function()
  2234. local east_api = meths.parse_expression(str, FLAGS_TO_STR[flags], true)
  2235. local east_hl = east_api.highlight
  2236. east_api.highlight = nil
  2237. local ast = simplify_east_api(str, east_api)
  2238. local hls = simplify_east_hl(str, east_hl)
  2239. local exps = {
  2240. ast = exp_ast,
  2241. hl_fs = exp_highlighting_fs,
  2242. }
  2243. local add_exps = nz_flags_exps[flags]
  2244. if not add_exps and flags == 3 + zflags then
  2245. add_exps = nz_flags_exps[1 + zflags] or nz_flags_exps[2 + zflags]
  2246. end
  2247. if add_exps then
  2248. if add_exps.ast then
  2249. exps.ast = mergedicts_copy(exps.ast, add_exps.ast)
  2250. end
  2251. if add_exps.hl_fs then
  2252. exps.hl_fs = mergedicts_copy(exps.hl_fs, add_exps.hl_fs)
  2253. end
  2254. end
  2255. eq(exps.ast, ast)
  2256. if exp_highlighting_fs then
  2257. local exp_highlighting = {}
  2258. local next_col = 0
  2259. for i, h in ipairs(exps.hl_fs) do
  2260. exp_highlighting[i], next_col = h(next_col)
  2261. end
  2262. eq(exp_highlighting, hls)
  2263. end
  2264. end)
  2265. if not err then
  2266. if type(msg) == 'table' then
  2267. local merr, new_msg = pcall(
  2268. format_string, 'table error:\n%s\n\n(%r)', msg.message, msg)
  2269. if merr then
  2270. msg = new_msg
  2271. else
  2272. msg = format_string('table error without .message:\n(%r)',
  2273. msg)
  2274. end
  2275. elseif type(msg) ~= 'string' then
  2276. msg = format_string('non-string non-table error:\n%r', msg)
  2277. end
  2278. error(format_string('Error while processing test (%r, %s):\n%s',
  2279. str, FLAGS_TO_STR[flags], msg))
  2280. end
  2281. end
  2282. end
  2283. local function hl(group, str, shift)
  2284. return function(next_col)
  2285. local col = next_col + (shift or 0)
  2286. return (('%s:%u:%u:%s'):format(
  2287. 'Nvim' .. group,
  2288. 0,
  2289. col,
  2290. str)), (col + #str)
  2291. end
  2292. end
  2293. local function fmtn(typ, args, rest)
  2294. if (typ == 'UnknownFigure'
  2295. or typ == 'DictLiteral'
  2296. or typ == 'CurlyBracesIdentifier'
  2297. or typ == 'Lambda') then
  2298. return ('%s%s'):format(typ, rest)
  2299. elseif typ == 'DoubleQuotedString' or typ == 'SingleQuotedString' then
  2300. if args:sub(-4) == 'NULL' then
  2301. args = args:sub(1, -5) .. '""'
  2302. end
  2303. return ('%s(%s)%s'):format(typ, args, rest)
  2304. end
  2305. end
  2306. require('test.unit.viml.expressions.parser_tests')(
  2307. it_maybe_pending, _check_parsing, hl, fmtn)
  2308. end)
  2309. describe('nvim_list_uis', function()
  2310. it('returns empty if --headless', function()
  2311. -- Test runner defaults to --headless.
  2312. eq({}, nvim("list_uis"))
  2313. end)
  2314. it('returns attached UIs', function()
  2315. local screen = Screen.new(20, 4)
  2316. screen:attach({override=true})
  2317. local expected = {
  2318. {
  2319. chan = 1,
  2320. ext_cmdline = false,
  2321. ext_popupmenu = false,
  2322. ext_tabline = false,
  2323. ext_wildmenu = false,
  2324. ext_linegrid = screen._options.ext_linegrid or false,
  2325. ext_multigrid = false,
  2326. ext_hlstate = false,
  2327. ext_termcolors = false,
  2328. ext_messages = false,
  2329. height = 4,
  2330. rgb = true,
  2331. override = true,
  2332. width = 20,
  2333. }
  2334. }
  2335. eq(expected, nvim("list_uis"))
  2336. screen:detach()
  2337. screen = Screen.new(44, 99)
  2338. screen:attach({ rgb = false })
  2339. expected[1].rgb = false
  2340. expected[1].override = false
  2341. expected[1].width = 44
  2342. expected[1].height = 99
  2343. eq(expected, nvim("list_uis"))
  2344. end)
  2345. end)
  2346. describe('nvim_create_namespace', function()
  2347. it('works', function()
  2348. eq({}, meths.get_namespaces())
  2349. eq(1, meths.create_namespace("ns-1"))
  2350. eq(2, meths.create_namespace("ns-2"))
  2351. eq(1, meths.create_namespace("ns-1"))
  2352. eq({["ns-1"]=1, ["ns-2"]=2}, meths.get_namespaces())
  2353. eq(3, meths.create_namespace(""))
  2354. eq(4, meths.create_namespace(""))
  2355. eq({["ns-1"]=1, ["ns-2"]=2}, meths.get_namespaces())
  2356. end)
  2357. end)
  2358. describe('nvim_create_buf', function()
  2359. it('works', function()
  2360. eq({id=2}, meths.create_buf(true, false))
  2361. eq({id=3}, meths.create_buf(false, false))
  2362. eq(' 1 %a "[No Name]" line 1\n'..
  2363. ' 2 h "[No Name]" line 0',
  2364. meths.command_output("ls"))
  2365. -- current buffer didn't change
  2366. eq({id=1}, meths.get_current_buf())
  2367. local screen = Screen.new(20, 4)
  2368. screen:attach()
  2369. meths.buf_set_lines(2, 0, -1, true, {"some text"})
  2370. meths.set_current_buf(2)
  2371. screen:expect([[
  2372. ^some text |
  2373. {1:~ }|
  2374. {1:~ }|
  2375. |
  2376. ]], {
  2377. [1] = {bold = true, foreground = Screen.colors.Blue1},
  2378. })
  2379. end)
  2380. it('can change buftype before visiting', function()
  2381. meths.set_option("hidden", false)
  2382. eq({id=2}, meths.create_buf(true, false))
  2383. meths.buf_set_option(2, "buftype", "nofile")
  2384. meths.buf_set_lines(2, 0, -1, true, {"test text"})
  2385. command("split | buffer 2")
  2386. eq({id=2}, meths.get_current_buf())
  2387. -- if the buf_set_option("buftype") didn't work, this would error out.
  2388. command("close")
  2389. eq({id=1}, meths.get_current_buf())
  2390. end)
  2391. it("does not trigger BufEnter, BufWinEnter", function()
  2392. command("let g:fired = v:false")
  2393. command("au BufEnter,BufWinEnter * let g:fired = v:true")
  2394. eq({id=2}, meths.create_buf(true, false))
  2395. meths.buf_set_lines(2, 0, -1, true, {"test", "text"})
  2396. eq(false, eval('g:fired'))
  2397. end)
  2398. it('scratch-buffer', function()
  2399. eq({id=2}, meths.create_buf(false, true))
  2400. eq({id=3}, meths.create_buf(true, true))
  2401. eq({id=4}, meths.create_buf(true, true))
  2402. local scratch_bufs = { 2, 3, 4 }
  2403. eq(' 1 %a "[No Name]" line 1\n'..
  2404. ' 3 h "[Scratch]" line 0\n'..
  2405. ' 4 h "[Scratch]" line 0',
  2406. meths.exec('ls', true))
  2407. -- current buffer didn't change
  2408. eq({id=1}, meths.get_current_buf())
  2409. local screen = Screen.new(20, 4)
  2410. screen:set_default_attr_ids({
  2411. [1] = {bold = true, foreground = Screen.colors.Blue1},
  2412. })
  2413. screen:attach()
  2414. --
  2415. -- Editing a scratch-buffer does NOT change its properties.
  2416. --
  2417. local edited_buf = 2
  2418. meths.buf_set_lines(edited_buf, 0, -1, true, {"some text"})
  2419. for _,b in ipairs(scratch_bufs) do
  2420. eq('nofile', meths.buf_get_option(b, 'buftype'))
  2421. eq('hide', meths.buf_get_option(b, 'bufhidden'))
  2422. eq(false, meths.buf_get_option(b, 'swapfile'))
  2423. eq(false, meths.buf_get_option(b, 'modeline'))
  2424. end
  2425. --
  2426. -- Visiting a scratch-buffer DOES NOT change its properties.
  2427. --
  2428. meths.set_current_buf(edited_buf)
  2429. screen:expect([[
  2430. ^some text |
  2431. {1:~ }|
  2432. {1:~ }|
  2433. |
  2434. ]])
  2435. eq('nofile', meths.buf_get_option(edited_buf, 'buftype'))
  2436. eq('hide', meths.buf_get_option(edited_buf, 'bufhidden'))
  2437. eq(false, meths.buf_get_option(edited_buf, 'swapfile'))
  2438. eq(false, meths.buf_get_option(edited_buf, 'modeline'))
  2439. -- Scratch buffer can be wiped without error.
  2440. command('bwipe')
  2441. screen:expect([[
  2442. ^ |
  2443. {1:~ }|
  2444. {1:~ }|
  2445. |
  2446. ]])
  2447. end)
  2448. it('does not cause heap-use-after-free on exit while setting options', function()
  2449. command('au OptionSet * q')
  2450. expect_exit(command, 'silent! call nvim_create_buf(0, 1)')
  2451. end)
  2452. end)
  2453. describe('nvim_get_runtime_file', function()
  2454. local p = helpers.alter_slashes
  2455. it('can find files', function()
  2456. eq({}, meths.get_runtime_file("bork.borkbork", false))
  2457. eq({}, meths.get_runtime_file("bork.borkbork", true))
  2458. eq(1, #meths.get_runtime_file("autoload/msgpack.vim", false))
  2459. eq(1, #meths.get_runtime_file("autoload/msgpack.vim", true))
  2460. local val = meths.get_runtime_file("autoload/remote/*.vim", true)
  2461. eq(2, #val)
  2462. if endswith(val[1], "define.vim") then
  2463. ok(endswith(val[1], p"autoload/remote/define.vim"))
  2464. ok(endswith(val[2], p"autoload/remote/host.vim"))
  2465. else
  2466. ok(endswith(val[1], p"autoload/remote/host.vim"))
  2467. ok(endswith(val[2], p"autoload/remote/define.vim"))
  2468. end
  2469. val = meths.get_runtime_file("autoload/remote/*.vim", false)
  2470. eq(1, #val)
  2471. ok(endswith(val[1], p"autoload/remote/define.vim")
  2472. or endswith(val[1], p"autoload/remote/host.vim"))
  2473. val = meths.get_runtime_file("lua", true)
  2474. eq(1, #val)
  2475. ok(endswith(val[1], p"lua"))
  2476. val = meths.get_runtime_file("lua/vim", true)
  2477. eq(1, #val)
  2478. ok(endswith(val[1], p"lua/vim"))
  2479. end)
  2480. it('can find directories', function()
  2481. local val = meths.get_runtime_file("lua/", true)
  2482. eq(1, #val)
  2483. ok(endswith(val[1], p"lua/"))
  2484. val = meths.get_runtime_file("lua/vim/", true)
  2485. eq(1, #val)
  2486. ok(endswith(val[1], p"lua/vim/"))
  2487. eq({}, meths.get_runtime_file("foobarlang/", true))
  2488. end)
  2489. it('can handle bad patterns', function()
  2490. if helpers.pending_win32(pending) then return end
  2491. eq("Vim:E220: Missing }.", pcall_err(meths.get_runtime_file, "{", false))
  2492. eq('Vim(echo):E5555: API call: Vim:E220: Missing }.',
  2493. exc_exec("echo nvim_get_runtime_file('{', v:false)"))
  2494. end)
  2495. end)
  2496. describe('nvim_get_all_options_info', function()
  2497. it('should have key value pairs of option names', function()
  2498. local options_info = meths.get_all_options_info()
  2499. neq(nil, options_info.listchars)
  2500. neq(nil, options_info.tabstop)
  2501. eq(meths.get_option_info'winhighlight', options_info.winhighlight)
  2502. end)
  2503. it('should not crash when echoed', function()
  2504. meths.exec("echo nvim_get_all_options_info()", true)
  2505. end)
  2506. end)
  2507. describe('nvim_get_option_info', function()
  2508. it('should error for unknown options', function()
  2509. eq("no such option: 'bogus'", pcall_err(meths.get_option_info, 'bogus'))
  2510. end)
  2511. it('should return the same options for short and long name', function()
  2512. eq(meths.get_option_info'winhl', meths.get_option_info'winhighlight')
  2513. end)
  2514. it('should have information about window options', function()
  2515. eq({
  2516. allows_duplicates = false,
  2517. commalist = true;
  2518. default = "";
  2519. flaglist = false;
  2520. global_local = false;
  2521. last_set_chan = 0;
  2522. last_set_linenr = 0;
  2523. last_set_sid = 0;
  2524. name = "winhighlight";
  2525. scope = "win";
  2526. shortname = "winhl";
  2527. type = "string";
  2528. was_set = false;
  2529. }, meths.get_option_info'winhl')
  2530. end)
  2531. it('should have information about buffer options', function()
  2532. eq({
  2533. allows_duplicates = true,
  2534. commalist = false,
  2535. default = "",
  2536. flaglist = false,
  2537. global_local = false,
  2538. last_set_chan = 0,
  2539. last_set_linenr = 0,
  2540. last_set_sid = 0,
  2541. name = "filetype",
  2542. scope = "buf",
  2543. shortname = "ft",
  2544. type = "string",
  2545. was_set = false
  2546. }, meths.get_option_info'filetype')
  2547. end)
  2548. it('should have information about global options', function()
  2549. -- precondition: the option was changed from its default
  2550. -- in test setup.
  2551. eq(false, meths.get_option'showcmd')
  2552. eq({
  2553. allows_duplicates = true,
  2554. commalist = false,
  2555. default = true,
  2556. flaglist = false,
  2557. global_local = false,
  2558. last_set_chan = 0,
  2559. last_set_linenr = 0,
  2560. last_set_sid = -2,
  2561. name = "showcmd",
  2562. scope = "global",
  2563. shortname = "sc",
  2564. type = "boolean",
  2565. was_set = true
  2566. }, meths.get_option_info'showcmd')
  2567. end)
  2568. end)
  2569. describe('nvim_echo', function()
  2570. local screen
  2571. before_each(function()
  2572. clear()
  2573. screen = Screen.new(40, 8)
  2574. screen:attach()
  2575. screen:set_default_attr_ids({
  2576. [0] = {bold=true, foreground=Screen.colors.Blue},
  2577. [1] = {bold = true, foreground = Screen.colors.SeaGreen},
  2578. [2] = {bold = true, reverse = true},
  2579. [3] = {foreground = Screen.colors.Brown, bold = true}, -- Statement
  2580. [4] = {foreground = Screen.colors.SlateBlue}, -- Special
  2581. })
  2582. command('highlight Statement gui=bold guifg=Brown')
  2583. command('highlight Special guifg=SlateBlue')
  2584. end)
  2585. it('should clear cmdline message before echo', function()
  2586. feed(':call nvim_echo([["msg"]], v:false, {})<CR>')
  2587. screen:expect{grid=[[
  2588. ^ |
  2589. {0:~ }|
  2590. {0:~ }|
  2591. {0:~ }|
  2592. {0:~ }|
  2593. {0:~ }|
  2594. {0:~ }|
  2595. msg |
  2596. ]]}
  2597. end)
  2598. it('can show highlighted line', function()
  2599. nvim_async("echo", {{"msg_a"}, {"msg_b", "Statement"}, {"msg_c", "Special"}}, true, {})
  2600. screen:expect{grid=[[
  2601. ^ |
  2602. {0:~ }|
  2603. {0:~ }|
  2604. {0:~ }|
  2605. {0:~ }|
  2606. {0:~ }|
  2607. {0:~ }|
  2608. msg_a{3:msg_b}{4:msg_c} |
  2609. ]]}
  2610. end)
  2611. it('can show highlighted multiline', function()
  2612. nvim_async("echo", {{"msg_a\nmsg_a", "Statement"}, {"msg_b", "Special"}}, true, {})
  2613. screen:expect{grid=[[
  2614. |
  2615. {0:~ }|
  2616. {0:~ }|
  2617. {0:~ }|
  2618. {2: }|
  2619. {3:msg_a} |
  2620. {3:msg_a}{4:msg_b} |
  2621. {1:Press ENTER or type command to continue}^ |
  2622. ]]}
  2623. end)
  2624. it('can save message history', function()
  2625. nvim('command', 'set cmdheight=2') -- suppress Press ENTER
  2626. nvim("echo", {{"msg\nmsg"}, {"msg"}}, true, {})
  2627. eq("msg\nmsgmsg", meths.exec('messages', true))
  2628. end)
  2629. it('can disable saving message history', function()
  2630. nvim('command', 'set cmdheight=2') -- suppress Press ENTER
  2631. nvim_async("echo", {{"msg\nmsg"}, {"msg"}}, false, {})
  2632. eq("", meths.exec("messages", true))
  2633. end)
  2634. end)
  2635. describe('nvim_open_term', function()
  2636. local screen
  2637. before_each(function()
  2638. clear()
  2639. screen = Screen.new(100, 35)
  2640. screen:attach()
  2641. screen:set_default_attr_ids({
  2642. [0] = {bold=true, foreground=Screen.colors.Blue},
  2643. [1] = {background = Screen.colors.Plum1};
  2644. [2] = {background = tonumber('0xffff40'), bg_indexed = true};
  2645. [3] = {background = Screen.colors.Plum1, fg_indexed = true, foreground = tonumber('0x00e000')};
  2646. [4] = {bold = true, reverse = true, background = Screen.colors.Plum1};
  2647. [5] = {foreground = Screen.colors.Blue, background = Screen.colors.LightMagenta, bold = true};
  2648. [6] = {bold = true};
  2649. [7] = {reverse = true, background = Screen.colors.LightMagenta};
  2650. })
  2651. end)
  2652. it('can batch process sequences', function()
  2653. local b = meths.create_buf(true,true)
  2654. meths.open_win(b, false, {width=79, height=31, row=1, col=1, relative='editor'})
  2655. local t = meths.open_term(b, {})
  2656. meths.chan_send(t, io.open("test/functional/fixtures/smile2.cat", "r"):read("*a"))
  2657. screen:expect{grid=[[
  2658. ^ |
  2659. {0:~}{1::smile }{0: }|
  2660. {0:~}{1: }{2:oooo$$$$$$$$$$$$oooo}{1: }{0: }|
  2661. {0:~}{1: }{2:oo$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{0: }|
  2662. {0:~}{1: }{2:oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{2:o$}{1: }{2:$$}{1: }{2:o$}{1: }{0: }|
  2663. {0:~}{1: }{2:o}{1: }{2:$}{1: }{2:oo}{1: }{2:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{2:$$}{1: }{2:$$}{1: }{2:$$o$}{1: }{0: }|
  2664. {0:~}{1: }{2:oo}{1: }{2:$}{1: }{2:$}{1: "}{2:$}{1: }{2:o$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$o}{1: }{2:$$$o$$o$}{1: }{0: }|
  2665. {0:~}{1: "}{2:$$$$$$o$}{1: }{2:o$$$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$o}{1: }{2:$$$$$$$$}{1: }{0: }|
  2666. {0:~}{1: }{2:$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$}{1: }{0: }|
  2667. {0:~}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$$}{1: """}{2:$$$}{1: }{0: }|
  2668. {0:~}{1: "}{2:$$$}{1:""""}{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: "}{2:$$$}{1: }{0: }|
  2669. {0:~}{1: }{2:$$$}{1: }{2:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: "}{2:$$$o}{1: }{0: }|
  2670. {0:~}{1: }{2:o$$}{1:" }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$o}{1: }{0: }|
  2671. {0:~}{1: }{2:$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1:" "}{2:$$$$$$ooooo$$$$o}{1: }{0: }|
  2672. {0:~}{1: }{2:o$$$oooo$$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:o$$$$$$$$$$$$$$$$$}{1: }{0: }|
  2673. {0:~}{1: }{2:$$$$$$$$}{1:"}{2:$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$$}{1:"""""""" }{0: }|
  2674. {0:~}{1: """" }{2:$$$$}{1: "}{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1:" }{2:o$$$}{1: }{0: }|
  2675. {0:~}{1: "}{2:$$$o}{1: """}{2:$$$$$$$$$$$$$$$$$$}{1:"}{2:$$}{1:" }{2:$$$}{1: }{0: }|
  2676. {0:~}{1: }{2:$$$o}{1: "}{2:$$}{1:""}{2:$$$$$$}{1:"""" }{2:o$$$}{1: }{0: }|
  2677. {0:~}{1: }{2:$$$$o}{1: }{2:o$$$}{1:" }{0: }|
  2678. {0:~}{1: "}{2:$$$$o}{1: }{2:o$$$$$$o}{1:"}{2:$$$$o}{1: }{2:o$$$$}{1: }{0: }|
  2679. {0:~}{1: "}{2:$$$$$oo}{1: ""}{2:$$$$o$$$$$o}{1: }{2:o$$$$}{1:"" }{0: }|
  2680. {0:~}{1: ""}{2:$$$$$oooo}{1: "}{2:$$$o$$$$$$$$$}{1:""" }{0: }|
  2681. {0:~}{1: ""}{2:$$$$$$$oo}{1: }{2:$$$$$$$$$$}{1: }{0: }|
  2682. {0:~}{1: """"}{2:$$$$$$$$$$$}{1: }{0: }|
  2683. {0:~}{1: }{2:$$$$$$$$$$$$}{1: }{0: }|
  2684. {0:~}{1: }{2:$$$$$$$$$$}{1:" }{0: }|
  2685. {0:~}{1: "}{2:$$$}{1:"""" }{0: }|
  2686. {0:~}{1: }{0: }|
  2687. {0:~}{3:Press ENTER or type command to continue}{1: }{0: }|
  2688. {0:~}{4:term://~/config2/docs/pres//32693:vim --clean +smile 29,39 All}{0: }|
  2689. {0:~}{1::call nvim__screenshot("smile2.cat") }{0: }|
  2690. {0:~ }|
  2691. {0:~ }|
  2692. |
  2693. ]]}
  2694. end)
  2695. it('can handle input', function()
  2696. screen:try_resize(50, 10)
  2697. eq({3, 2}, exec_lua [[
  2698. buf = vim.api.nvim_create_buf(1,1)
  2699. stream = ''
  2700. do_the_echo = false
  2701. function input(_,t1,b1,data)
  2702. stream = stream .. data
  2703. _G.vals = {t1, b1}
  2704. if do_the_echo then
  2705. vim.api.nvim_chan_send(t1, data)
  2706. end
  2707. end
  2708. term = vim.api.nvim_open_term(buf, {on_input=input})
  2709. vim.api.nvim_open_win(buf, true, {width=40, height=5, row=1, col=1, relative='editor'})
  2710. return {term, buf}
  2711. ]])
  2712. screen:expect{grid=[[
  2713. |
  2714. {0:~}{1:^ }{0: }|
  2715. {0:~}{1: }{0: }|
  2716. {0:~}{1: }{0: }|
  2717. {0:~}{1: }{0: }|
  2718. {0:~}{1: }{0: }|
  2719. {0:~ }|
  2720. {0:~ }|
  2721. {0:~ }|
  2722. |
  2723. ]]}
  2724. feed 'iba<c-x>bla'
  2725. screen:expect{grid=[[
  2726. |
  2727. {0:~}{7: }{1: }{0: }|
  2728. {0:~}{1: }{0: }|
  2729. {0:~}{1: }{0: }|
  2730. {0:~}{1: }{0: }|
  2731. {0:~}{1: }{0: }|
  2732. {0:~ }|
  2733. {0:~ }|
  2734. {0:~ }|
  2735. {6:-- TERMINAL --} |
  2736. ]]}
  2737. eq('ba\024bla', exec_lua [[ return stream ]])
  2738. eq({3,2}, exec_lua [[ return vals ]])
  2739. exec_lua [[ do_the_echo = true ]]
  2740. feed 'herrejösses!'
  2741. screen:expect{grid=[[
  2742. |
  2743. {0:~}{1:herrejösses!}{7: }{1: }{0: }|
  2744. {0:~}{1: }{0: }|
  2745. {0:~}{1: }{0: }|
  2746. {0:~}{1: }{0: }|
  2747. {0:~}{1: }{0: }|
  2748. {0:~ }|
  2749. {0:~ }|
  2750. {0:~ }|
  2751. {6:-- TERMINAL --} |
  2752. ]]}
  2753. eq('ba\024blaherrejösses!', exec_lua [[ return stream ]])
  2754. end)
  2755. end)
  2756. describe('nvim_del_mark', function()
  2757. it('works', function()
  2758. local buf = meths.create_buf(false,true)
  2759. meths.buf_set_lines(buf, -1, -1, true, {'a', 'bit of', 'text'})
  2760. eq(true, meths.buf_set_mark(buf, 'F', 2, 2, {}))
  2761. eq(true, meths.del_mark('F'))
  2762. eq({0, 0}, meths.buf_get_mark(buf, 'F'))
  2763. end)
  2764. it('fails when invalid marks are used', function()
  2765. eq(false, pcall(meths.del_mark, 'f'))
  2766. eq(false, pcall(meths.del_mark, '!'))
  2767. eq(false, pcall(meths.del_mark, 'fail'))
  2768. end)
  2769. end)
  2770. describe('nvim_get_mark', function()
  2771. it('works', function()
  2772. local buf = meths.create_buf(false,true)
  2773. meths.buf_set_lines(buf, -1, -1, true, {'a', 'bit of', 'text'})
  2774. meths.buf_set_mark(buf, 'F', 2, 2, {})
  2775. meths.buf_set_name(buf, "mybuf")
  2776. local mark = meths.get_mark('F', {})
  2777. -- Compare the path tail ony
  2778. assert(string.find(mark[4], "mybuf$"))
  2779. eq({2, 2, buf.id, mark[4]}, mark)
  2780. end)
  2781. it('fails when invalid marks are used', function()
  2782. eq(false, pcall(meths.del_mark, 'f'))
  2783. eq(false, pcall(meths.del_mark, '!'))
  2784. eq(false, pcall(meths.del_mark, 'fail'))
  2785. end)
  2786. it('returns the expected when mark is not set', function()
  2787. eq(true, meths.del_mark('A'))
  2788. eq({0, 0, 0, ''}, meths.get_mark('A', {}))
  2789. end)
  2790. it('works with deleted buffers', function()
  2791. local fname = tmpname()
  2792. write_file(fname, 'a\nbit of\text')
  2793. nvim("command", "edit " .. fname)
  2794. local buf = meths.get_current_buf()
  2795. meths.buf_set_mark(buf, 'F', 2, 2, {})
  2796. nvim("command", "new") -- Create new buf to avoid :bd failing
  2797. nvim("command", "bd! " .. buf.id)
  2798. os.remove(fname)
  2799. local mark = meths.get_mark('F', {})
  2800. -- To avoid comparing relative vs absolute path
  2801. local mfname = mark[4]
  2802. local tail_patt = [[[\/][^\/]*$]]
  2803. -- tail of paths should be equals
  2804. eq(fname:match(tail_patt), mfname:match(tail_patt))
  2805. eq({2, 2, buf.id, mark[4]}, mark)
  2806. end)
  2807. end)
  2808. describe('nvim_eval_statusline', function()
  2809. it('works', function()
  2810. eq({
  2811. str = '%StatusLineStringWithHighlights',
  2812. width = 31
  2813. },
  2814. meths.eval_statusline(
  2815. '%%StatusLineString%#WarningMsg#WithHighlights',
  2816. {}))
  2817. end)
  2818. it('doesn\'t exceed maxwidth', function()
  2819. eq({
  2820. str = 'Should be trun>',
  2821. width = 15
  2822. },
  2823. meths.eval_statusline(
  2824. 'Should be truncated%<',
  2825. { maxwidth = 15 }))
  2826. end)
  2827. it('supports ASCII fillchar', function()
  2828. eq({ str = 'a~~~b', width = 5 },
  2829. meths.eval_statusline('a%=b', { fillchar = '~', maxwidth = 5 }))
  2830. end)
  2831. it('supports single-width multibyte fillchar', function()
  2832. eq({ str = 'a━━━b', width = 5 },
  2833. meths.eval_statusline('a%=b', { fillchar = '━', maxwidth = 5 }))
  2834. end)
  2835. it('treats double-width fillchar as single-width', function()
  2836. eq({ str = 'a哦哦哦b', width = 5 },
  2837. meths.eval_statusline('a%=b', { fillchar = '哦', maxwidth = 5 }))
  2838. end)
  2839. it('treats control character fillchar as single-width', function()
  2840. eq({ str = 'a\031\031\031b', width = 5 },
  2841. meths.eval_statusline('a%=b', { fillchar = '\031', maxwidth = 5 }))
  2842. end)
  2843. it('rejects multiple-character fillchar', function()
  2844. eq('fillchar must be a single character',
  2845. pcall_err(meths.eval_statusline, '', { fillchar = 'aa' }))
  2846. end)
  2847. it('rejects empty string fillchar', function()
  2848. eq('fillchar must be a single character',
  2849. pcall_err(meths.eval_statusline, '', { fillchar = '' }))
  2850. end)
  2851. it('rejects non-string fillchar', function()
  2852. eq('fillchar must be a single character',
  2853. pcall_err(meths.eval_statusline, '', { fillchar = 1 }))
  2854. end)
  2855. it('rejects invalid string', function()
  2856. eq('E539: Illegal character <}>',
  2857. pcall_err(meths.eval_statusline, '%{%}', {}))
  2858. end)
  2859. describe('highlight parsing', function()
  2860. it('works', function()
  2861. eq({
  2862. str = "TextWithWarningHighlightTextWithUserHighlight",
  2863. width = 45,
  2864. highlights = {
  2865. { start = 0, group = 'WarningMsg' },
  2866. { start = 24, group = 'User1' }
  2867. },
  2868. },
  2869. meths.eval_statusline(
  2870. '%#WarningMsg#TextWithWarningHighlight%1*TextWithUserHighlight',
  2871. { highlights = true }))
  2872. end)
  2873. it('works with no highlight', function()
  2874. eq({
  2875. str = "TextWithNoHighlight",
  2876. width = 19,
  2877. highlights = {
  2878. { start = 0, group = 'StatusLine' },
  2879. },
  2880. },
  2881. meths.eval_statusline(
  2882. 'TextWithNoHighlight',
  2883. { highlights = true }))
  2884. end)
  2885. it('works with inactive statusline', function()
  2886. command('split')
  2887. eq({
  2888. str = 'TextWithNoHighlightTextWithWarningHighlight',
  2889. width = 43,
  2890. highlights = {
  2891. { start = 0, group = 'StatusLineNC' },
  2892. { start = 19, group = 'WarningMsg' }
  2893. }
  2894. },
  2895. meths.eval_statusline(
  2896. 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
  2897. { winid = meths.list_wins()[2].id, highlights = true }))
  2898. end)
  2899. it('works with tabline', function()
  2900. eq({
  2901. str = 'TextWithNoHighlightTextWithWarningHighlight',
  2902. width = 43,
  2903. highlights = {
  2904. { start = 0, group = 'TabLineFill' },
  2905. { start = 19, group = 'WarningMsg' }
  2906. }
  2907. },
  2908. meths.eval_statusline(
  2909. 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
  2910. { use_tabline = true, highlights = true }))
  2911. end)
  2912. it('works with winbar', function()
  2913. eq({
  2914. str = 'TextWithNoHighlightTextWithWarningHighlight',
  2915. width = 43,
  2916. highlights = {
  2917. { start = 0, group = 'WinBar' },
  2918. { start = 19, group = 'WarningMsg' }
  2919. }
  2920. },
  2921. meths.eval_statusline(
  2922. 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
  2923. { use_winbar = true, highlights = true }))
  2924. end)
  2925. end)
  2926. end)
  2927. describe('nvim_parse_cmd', function()
  2928. it('works', function()
  2929. eq({
  2930. cmd = 'echo',
  2931. args = { 'foo' },
  2932. bang = false,
  2933. addr = 'none',
  2934. magic = {
  2935. file = false,
  2936. bar = false
  2937. },
  2938. nargs = '*',
  2939. nextcmd = '',
  2940. mods = {
  2941. browse = false,
  2942. confirm = false,
  2943. emsg_silent = false,
  2944. filter = {
  2945. pattern = "",
  2946. force = false
  2947. },
  2948. hide = false,
  2949. horizontal = false,
  2950. keepalt = false,
  2951. keepjumps = false,
  2952. keepmarks = false,
  2953. keeppatterns = false,
  2954. lockmarks = false,
  2955. noautocmd = false,
  2956. noswapfile = false,
  2957. sandbox = false,
  2958. silent = false,
  2959. split = "",
  2960. tab = -1,
  2961. unsilent = false,
  2962. verbose = -1,
  2963. vertical = false,
  2964. }
  2965. }, meths.parse_cmd('echo foo', {}))
  2966. end)
  2967. it('works with ranges', function()
  2968. eq({
  2969. cmd = 'substitute',
  2970. args = { '/math.random/math.max/' },
  2971. bang = false,
  2972. range = { 4, 6 },
  2973. addr = 'line',
  2974. magic = {
  2975. file = false,
  2976. bar = false
  2977. },
  2978. nargs = '*',
  2979. nextcmd = '',
  2980. mods = {
  2981. browse = false,
  2982. confirm = false,
  2983. emsg_silent = false,
  2984. filter = {
  2985. pattern = "",
  2986. force = false
  2987. },
  2988. hide = false,
  2989. horizontal = false,
  2990. keepalt = false,
  2991. keepjumps = false,
  2992. keepmarks = false,
  2993. keeppatterns = false,
  2994. lockmarks = false,
  2995. noautocmd = false,
  2996. noswapfile = false,
  2997. sandbox = false,
  2998. silent = false,
  2999. split = "",
  3000. tab = -1,
  3001. unsilent = false,
  3002. verbose = -1,
  3003. vertical = false,
  3004. }
  3005. }, meths.parse_cmd('4,6s/math.random/math.max/', {}))
  3006. end)
  3007. it('works with count', function()
  3008. eq({
  3009. cmd = 'buffer',
  3010. args = {},
  3011. bang = false,
  3012. range = { 1 },
  3013. count = 1,
  3014. addr = 'buf',
  3015. magic = {
  3016. file = false,
  3017. bar = true
  3018. },
  3019. nargs = '*',
  3020. nextcmd = '',
  3021. mods = {
  3022. browse = false,
  3023. confirm = false,
  3024. emsg_silent = false,
  3025. filter = {
  3026. pattern = "",
  3027. force = false
  3028. },
  3029. hide = false,
  3030. horizontal = false,
  3031. keepalt = false,
  3032. keepjumps = false,
  3033. keepmarks = false,
  3034. keeppatterns = false,
  3035. lockmarks = false,
  3036. noautocmd = false,
  3037. noswapfile = false,
  3038. sandbox = false,
  3039. silent = false,
  3040. split = "",
  3041. tab = -1,
  3042. unsilent = false,
  3043. verbose = -1,
  3044. vertical = false,
  3045. }
  3046. }, meths.parse_cmd('buffer 1', {}))
  3047. end)
  3048. it('works with register', function()
  3049. eq({
  3050. cmd = 'put',
  3051. args = {},
  3052. bang = false,
  3053. range = {},
  3054. reg = '+',
  3055. addr = 'line',
  3056. magic = {
  3057. file = false,
  3058. bar = true
  3059. },
  3060. nargs = '0',
  3061. nextcmd = '',
  3062. mods = {
  3063. browse = false,
  3064. confirm = false,
  3065. emsg_silent = false,
  3066. filter = {
  3067. pattern = "",
  3068. force = false
  3069. },
  3070. hide = false,
  3071. horizontal = false,
  3072. keepalt = false,
  3073. keepjumps = false,
  3074. keepmarks = false,
  3075. keeppatterns = false,
  3076. lockmarks = false,
  3077. noautocmd = false,
  3078. noswapfile = false,
  3079. sandbox = false,
  3080. silent = false,
  3081. split = "",
  3082. tab = -1,
  3083. unsilent = false,
  3084. verbose = -1,
  3085. vertical = false,
  3086. }
  3087. }, meths.parse_cmd('put +', {}))
  3088. eq({
  3089. cmd = 'put',
  3090. args = {},
  3091. bang = false,
  3092. range = {},
  3093. reg = '',
  3094. addr = 'line',
  3095. magic = {
  3096. file = false,
  3097. bar = true
  3098. },
  3099. nargs = '0',
  3100. nextcmd = '',
  3101. mods = {
  3102. browse = false,
  3103. confirm = false,
  3104. emsg_silent = false,
  3105. filter = {
  3106. pattern = "",
  3107. force = false
  3108. },
  3109. hide = false,
  3110. horizontal = false,
  3111. keepalt = false,
  3112. keepjumps = false,
  3113. keepmarks = false,
  3114. keeppatterns = false,
  3115. lockmarks = false,
  3116. noautocmd = false,
  3117. noswapfile = false,
  3118. sandbox = false,
  3119. silent = false,
  3120. split = "",
  3121. tab = -1,
  3122. unsilent = false,
  3123. verbose = -1,
  3124. vertical = false,
  3125. }
  3126. }, meths.parse_cmd('put', {}))
  3127. end)
  3128. it('works with range, count and register', function()
  3129. eq({
  3130. cmd = 'delete',
  3131. args = {},
  3132. bang = false,
  3133. range = { 3, 7 },
  3134. count = 7,
  3135. reg = '*',
  3136. addr = 'line',
  3137. magic = {
  3138. file = false,
  3139. bar = true
  3140. },
  3141. nargs = '0',
  3142. nextcmd = '',
  3143. mods = {
  3144. browse = false,
  3145. confirm = false,
  3146. emsg_silent = false,
  3147. filter = {
  3148. pattern = "",
  3149. force = false
  3150. },
  3151. hide = false,
  3152. horizontal = false,
  3153. keepalt = false,
  3154. keepjumps = false,
  3155. keepmarks = false,
  3156. keeppatterns = false,
  3157. lockmarks = false,
  3158. noautocmd = false,
  3159. noswapfile = false,
  3160. sandbox = false,
  3161. silent = false,
  3162. split = "",
  3163. tab = -1,
  3164. unsilent = false,
  3165. verbose = -1,
  3166. vertical = false,
  3167. }
  3168. }, meths.parse_cmd('1,3delete * 5', {}))
  3169. end)
  3170. it('works with bang', function()
  3171. eq({
  3172. cmd = 'write',
  3173. args = {},
  3174. bang = true,
  3175. range = {},
  3176. addr = 'line',
  3177. magic = {
  3178. file = true,
  3179. bar = true
  3180. },
  3181. nargs = '?',
  3182. nextcmd = '',
  3183. mods = {
  3184. browse = false,
  3185. confirm = false,
  3186. emsg_silent = false,
  3187. filter = {
  3188. pattern = "",
  3189. force = false
  3190. },
  3191. hide = false,
  3192. horizontal = false,
  3193. keepalt = false,
  3194. keepjumps = false,
  3195. keepmarks = false,
  3196. keeppatterns = false,
  3197. lockmarks = false,
  3198. noautocmd = false,
  3199. noswapfile = false,
  3200. sandbox = false,
  3201. silent = false,
  3202. split = "",
  3203. tab = -1,
  3204. unsilent = false,
  3205. verbose = -1,
  3206. vertical = false,
  3207. },
  3208. }, meths.parse_cmd('w!', {}))
  3209. end)
  3210. it('works with modifiers', function()
  3211. eq({
  3212. cmd = 'split',
  3213. args = { 'foo.txt' },
  3214. bang = false,
  3215. range = {},
  3216. addr = '?',
  3217. magic = {
  3218. file = true,
  3219. bar = true
  3220. },
  3221. nargs = '?',
  3222. nextcmd = '',
  3223. mods = {
  3224. browse = false,
  3225. confirm = false,
  3226. emsg_silent = true,
  3227. filter = {
  3228. pattern = "foo",
  3229. force = false
  3230. },
  3231. hide = false,
  3232. horizontal = true,
  3233. keepalt = false,
  3234. keepjumps = false,
  3235. keepmarks = false,
  3236. keeppatterns = false,
  3237. lockmarks = false,
  3238. noautocmd = false,
  3239. noswapfile = false,
  3240. sandbox = false,
  3241. silent = true,
  3242. split = "topleft",
  3243. tab = 1,
  3244. unsilent = false,
  3245. verbose = 15,
  3246. vertical = false,
  3247. },
  3248. }, meths.parse_cmd('15verbose silent! horizontal topleft tab filter /foo/ split foo.txt', {}))
  3249. eq({
  3250. cmd = 'split',
  3251. args = { 'foo.txt' },
  3252. bang = false,
  3253. range = {},
  3254. addr = '?',
  3255. magic = {
  3256. file = true,
  3257. bar = true
  3258. },
  3259. nargs = '?',
  3260. nextcmd = '',
  3261. mods = {
  3262. browse = false,
  3263. confirm = true,
  3264. emsg_silent = false,
  3265. filter = {
  3266. pattern = "foo",
  3267. force = true
  3268. },
  3269. hide = false,
  3270. horizontal = false,
  3271. keepalt = false,
  3272. keepjumps = false,
  3273. keepmarks = false,
  3274. keeppatterns = false,
  3275. lockmarks = false,
  3276. noautocmd = false,
  3277. noswapfile = false,
  3278. sandbox = false,
  3279. silent = false,
  3280. split = "botright",
  3281. tab = 0,
  3282. unsilent = true,
  3283. verbose = 0,
  3284. vertical = false,
  3285. },
  3286. }, meths.parse_cmd('0verbose unsilent botright 0tab confirm filter! /foo/ split foo.txt', {}))
  3287. end)
  3288. it('works with user commands', function()
  3289. command('command -bang -nargs=+ -range -addr=lines MyCommand echo foo')
  3290. eq({
  3291. cmd = 'MyCommand',
  3292. args = { 'test', 'it' },
  3293. bang = true,
  3294. range = { 4, 6 },
  3295. addr = 'line',
  3296. magic = {
  3297. file = false,
  3298. bar = false
  3299. },
  3300. nargs = '+',
  3301. nextcmd = '',
  3302. mods = {
  3303. browse = false,
  3304. confirm = false,
  3305. emsg_silent = false,
  3306. filter = {
  3307. pattern = "",
  3308. force = false
  3309. },
  3310. hide = false,
  3311. horizontal = false,
  3312. keepalt = false,
  3313. keepjumps = false,
  3314. keepmarks = false,
  3315. keeppatterns = false,
  3316. lockmarks = false,
  3317. noautocmd = false,
  3318. noswapfile = false,
  3319. sandbox = false,
  3320. silent = false,
  3321. split = "",
  3322. tab = -1,
  3323. unsilent = false,
  3324. verbose = -1,
  3325. vertical = false,
  3326. }
  3327. }, meths.parse_cmd('4,6MyCommand! test it', {}))
  3328. end)
  3329. it('works for commands separated by bar', function()
  3330. eq({
  3331. cmd = 'argadd',
  3332. args = { 'a.txt' },
  3333. bang = false,
  3334. range = {},
  3335. addr = 'arg',
  3336. magic = {
  3337. file = true,
  3338. bar = true
  3339. },
  3340. nargs = '*',
  3341. nextcmd = 'argadd b.txt',
  3342. mods = {
  3343. browse = false,
  3344. confirm = false,
  3345. emsg_silent = false,
  3346. filter = {
  3347. pattern = "",
  3348. force = false
  3349. },
  3350. hide = false,
  3351. horizontal = false,
  3352. keepalt = false,
  3353. keepjumps = false,
  3354. keepmarks = false,
  3355. keeppatterns = false,
  3356. lockmarks = false,
  3357. noautocmd = false,
  3358. noswapfile = false,
  3359. sandbox = false,
  3360. silent = false,
  3361. split = "",
  3362. tab = -1,
  3363. unsilent = false,
  3364. verbose = -1,
  3365. vertical = false,
  3366. }
  3367. }, meths.parse_cmd('argadd a.txt | argadd b.txt', {}))
  3368. end)
  3369. it('works for nargs=1', function()
  3370. command('command -nargs=1 MyCommand echo <q-args>')
  3371. eq({
  3372. cmd = 'MyCommand',
  3373. args = { 'test it' },
  3374. bang = false,
  3375. addr = 'none',
  3376. magic = {
  3377. file = false,
  3378. bar = false
  3379. },
  3380. nargs = '1',
  3381. nextcmd = '',
  3382. mods = {
  3383. browse = false,
  3384. confirm = false,
  3385. emsg_silent = false,
  3386. filter = {
  3387. pattern = "",
  3388. force = false
  3389. },
  3390. hide = false,
  3391. horizontal = false,
  3392. keepalt = false,
  3393. keepjumps = false,
  3394. keepmarks = false,
  3395. keeppatterns = false,
  3396. lockmarks = false,
  3397. noautocmd = false,
  3398. noswapfile = false,
  3399. sandbox = false,
  3400. silent = false,
  3401. split = "",
  3402. tab = -1,
  3403. unsilent = false,
  3404. verbose = -1,
  3405. vertical = false,
  3406. }
  3407. }, meths.parse_cmd('MyCommand test it', {}))
  3408. end)
  3409. it('errors for invalid command', function()
  3410. eq('Error while parsing command line', pcall_err(meths.parse_cmd, '', {}))
  3411. eq('Error while parsing command line', pcall_err(meths.parse_cmd, '" foo', {}))
  3412. eq('Error while parsing command line: E492: Not an editor command: Fubar',
  3413. pcall_err(meths.parse_cmd, 'Fubar', {}))
  3414. command('command! Fubar echo foo')
  3415. eq('Error while parsing command line: E477: No ! allowed',
  3416. pcall_err(meths.parse_cmd, 'Fubar!', {}))
  3417. eq('Error while parsing command line: E481: No range allowed',
  3418. pcall_err(meths.parse_cmd, '4,6Fubar', {}))
  3419. command('command! Foobar echo foo')
  3420. eq('Error while parsing command line: E464: Ambiguous use of user-defined command',
  3421. pcall_err(meths.parse_cmd, 'F', {}))
  3422. end)
  3423. it('does not interfere with printing line in Ex mode #19400', function()
  3424. local screen = Screen.new(60, 7)
  3425. screen:set_default_attr_ids({
  3426. [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
  3427. [1] = {bold = true, reverse = true}, -- MsgSeparator
  3428. })
  3429. screen:attach()
  3430. insert([[
  3431. foo
  3432. bar]])
  3433. feed('gQ1')
  3434. screen:expect([[
  3435. foo |
  3436. bar |
  3437. {0:~ }|
  3438. {0:~ }|
  3439. {1: }|
  3440. Entering Ex mode. Type "visual" to go to Normal mode. |
  3441. :1^ |
  3442. ]])
  3443. eq('Error while parsing command line', pcall_err(meths.parse_cmd, '', {}))
  3444. feed('<CR>')
  3445. screen:expect([[
  3446. foo |
  3447. bar |
  3448. {1: }|
  3449. Entering Ex mode. Type "visual" to go to Normal mode. |
  3450. :1 |
  3451. foo |
  3452. :^ |
  3453. ]])
  3454. end)
  3455. it('does not move cursor or change search history/pattern #19878 #19890', function()
  3456. meths.buf_set_lines(0, 0, -1, true, {'foo', 'bar', 'foo', 'bar'})
  3457. eq({1, 0}, meths.win_get_cursor(0))
  3458. eq('', funcs.getreg('/'))
  3459. eq('', funcs.histget('search'))
  3460. feed(':') -- call the API in cmdline mode to test whether it changes search history
  3461. eq({
  3462. cmd = 'normal',
  3463. args = {'x'},
  3464. bang = true,
  3465. range = {3, 4},
  3466. addr = 'line',
  3467. magic = {
  3468. file = false,
  3469. bar = false,
  3470. },
  3471. nargs = '+',
  3472. nextcmd = '',
  3473. mods = {
  3474. browse = false,
  3475. confirm = false,
  3476. emsg_silent = false,
  3477. filter = {
  3478. pattern = "",
  3479. force = false,
  3480. },
  3481. hide = false,
  3482. horizontal = false,
  3483. keepalt = false,
  3484. keepjumps = false,
  3485. keepmarks = false,
  3486. keeppatterns = false,
  3487. lockmarks = false,
  3488. noautocmd = false,
  3489. noswapfile = false,
  3490. sandbox = false,
  3491. silent = false,
  3492. split = "",
  3493. tab = -1,
  3494. unsilent = false,
  3495. verbose = -1,
  3496. vertical = false,
  3497. }
  3498. }, meths.parse_cmd('+2;/bar/normal! x', {}))
  3499. eq({1, 0}, meths.win_get_cursor(0))
  3500. eq('', funcs.getreg('/'))
  3501. eq('', funcs.histget('search'))
  3502. end)
  3503. it('result can be used directly by nvim_cmd #20051', function()
  3504. eq("foo", meths.cmd(meths.parse_cmd('echo "foo"', {}), { output = true }))
  3505. meths.cmd(meths.parse_cmd("set cursorline", {}), {})
  3506. eq(true, meths.get_option_value("cursorline", {}))
  3507. end)
  3508. end)
  3509. describe('nvim_cmd', function()
  3510. it('works', function ()
  3511. meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
  3512. eq(true, meths.get_option_value("cursorline", {}))
  3513. end)
  3514. it('captures output', function()
  3515. eq("foo", meths.cmd({ cmd = "echo", args = { '"foo"' } }, { output = true }))
  3516. end)
  3517. it('sets correct script context', function()
  3518. meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
  3519. local str = meths.exec([[verbose set cursorline?]], true)
  3520. neq(nil, str:find("cursorline\n\tLast set from API client %(channel id %d+%)"))
  3521. end)
  3522. it('works with range', function()
  3523. insert [[
  3524. line1
  3525. line2
  3526. line3
  3527. line4
  3528. you didn't expect this
  3529. line5
  3530. line6
  3531. ]]
  3532. meths.cmd({ cmd = "del", range = {2, 4} }, {})
  3533. expect [[
  3534. line1
  3535. you didn't expect this
  3536. line5
  3537. line6
  3538. ]]
  3539. end)
  3540. it('works with count', function()
  3541. insert [[
  3542. line1
  3543. line2
  3544. line3
  3545. line4
  3546. you didn't expect this
  3547. line5
  3548. line6
  3549. ]]
  3550. meths.cmd({ cmd = "del", range = { 2 }, count = 4 }, {})
  3551. expect [[
  3552. line1
  3553. line5
  3554. line6
  3555. ]]
  3556. end)
  3557. it('works with register', function()
  3558. insert [[
  3559. line1
  3560. line2
  3561. line3
  3562. line4
  3563. you didn't expect this
  3564. line5
  3565. line6
  3566. ]]
  3567. meths.cmd({ cmd = "del", range = { 2, 4 }, reg = 'a' }, {})
  3568. meths.exec("1put a", false)
  3569. expect [[
  3570. line1
  3571. line2
  3572. line3
  3573. line4
  3574. you didn't expect this
  3575. line5
  3576. line6
  3577. ]]
  3578. end)
  3579. it('works with bang', function ()
  3580. meths.create_user_command("Foo", 'echo "<bang>"', { bang = true })
  3581. eq("!", meths.cmd({ cmd = "Foo", bang = true }, { output = true }))
  3582. eq("", meths.cmd({ cmd = "Foo", bang = false }, { output = true }))
  3583. end)
  3584. it('works with modifiers', function()
  3585. -- with silent = true output is still captured
  3586. eq('1',
  3587. meths.cmd({ cmd = 'echomsg', args = { '1' }, mods = { silent = true } },
  3588. { output = true }))
  3589. -- but message isn't added to message history
  3590. eq('', meths.cmd({ cmd = 'messages' }, { output = true }))
  3591. meths.create_user_command("Foo", 'set verbose', {})
  3592. eq(" verbose=1", meths.cmd({ cmd = "Foo", mods = { verbose = 1 } }, { output = true }))
  3593. meths.create_user_command("Mods", "echo '<mods>'", {})
  3594. eq('keepmarks keeppatterns silent 3verbose aboveleft horizontal',
  3595. meths.cmd({ cmd = "Mods", mods = {
  3596. horizontal = true,
  3597. keepmarks = true,
  3598. keeppatterns = true,
  3599. silent = true,
  3600. split = 'aboveleft',
  3601. verbose = 3,
  3602. } }, { output = true }))
  3603. eq(0, meths.get_option_value("verbose", {}))
  3604. command('edit foo.txt | edit bar.txt')
  3605. eq(' 1 #h "foo.txt" line 1',
  3606. meths.cmd({ cmd = "buffers", mods = { filter = { pattern = "foo", force = false } } },
  3607. { output = true }))
  3608. eq(' 2 %a "bar.txt" line 1',
  3609. meths.cmd({ cmd = "buffers", mods = { filter = { pattern = "foo", force = true } } },
  3610. { output = true }))
  3611. -- with emsg_silent = true error is suppresed
  3612. feed([[:lua vim.api.nvim_cmd({ cmd = 'call', mods = { emsg_silent = true } }, {})<CR>]])
  3613. eq('', meths.cmd({ cmd = 'messages' }, { output = true }))
  3614. -- error from the next command typed is not suppressed #21420
  3615. feed(':call<CR><CR>')
  3616. eq('E471: Argument required', meths.cmd({ cmd = 'messages' }, { output = true }))
  3617. end)
  3618. it('works with magic.file', function()
  3619. exec_lua([[
  3620. vim.api.nvim_create_user_command("Foo", function(opts)
  3621. vim.api.nvim_echo({{ opts.fargs[1] }}, false, {})
  3622. end, { nargs = 1 })
  3623. ]])
  3624. eq(lfs.currentdir(),
  3625. meths.cmd({ cmd = "Foo", args = { '%:p:h' }, magic = { file = true } },
  3626. { output = true }))
  3627. end)
  3628. it('splits arguments correctly', function()
  3629. meths.exec([[
  3630. function! FooFunc(...)
  3631. echo a:000
  3632. endfunction
  3633. ]], false)
  3634. meths.create_user_command("Foo", "call FooFunc(<f-args>)", { nargs = '+' })
  3635. eq([=[['a quick', 'brown fox', 'jumps over the', 'lazy dog']]=],
  3636. meths.cmd({ cmd = "Foo", args = { "a quick", "brown fox", "jumps over the", "lazy dog"}},
  3637. { output = true }))
  3638. eq([=[['test \ \\ \"""\', 'more\ tests\" ']]=],
  3639. meths.cmd({ cmd = "Foo", args = { [[test \ \\ \"""\]], [[more\ tests\" ]] } },
  3640. { output = true }))
  3641. end)
  3642. it('splits arguments correctly for Lua callback', function()
  3643. meths.exec_lua([[
  3644. local function FooFunc(opts)
  3645. vim.pretty_print(opts.fargs)
  3646. end
  3647. vim.api.nvim_create_user_command("Foo", FooFunc, { nargs = '+' })
  3648. ]], {})
  3649. eq([[{ "a quick", "brown fox", "jumps over the", "lazy dog" }]],
  3650. meths.cmd({ cmd = "Foo", args = { "a quick", "brown fox", "jumps over the", "lazy dog"}},
  3651. { output = true }))
  3652. eq([[{ 'test \\ \\\\ \\"""\\', 'more\\ tests\\" ' }]],
  3653. meths.cmd({ cmd = "Foo", args = { [[test \ \\ \"""\]], [[more\ tests\" ]] } },
  3654. { output = true }))
  3655. end)
  3656. it('works with buffer names', function()
  3657. command("edit foo.txt | edit bar.txt")
  3658. meths.cmd({ cmd = "buffer", args = { "foo.txt" } }, {})
  3659. eq("foo.txt", funcs.fnamemodify(meths.buf_get_name(0), ":t"))
  3660. meths.cmd({ cmd = "buffer", args = { "bar.txt" } }, {})
  3661. eq("bar.txt", funcs.fnamemodify(meths.buf_get_name(0), ":t"))
  3662. end)
  3663. it('triggers CmdUndefined event if command is not found', function()
  3664. meths.exec_lua([[
  3665. vim.api.nvim_create_autocmd("CmdUndefined",
  3666. { pattern = "Foo",
  3667. callback = function()
  3668. vim.api.nvim_create_user_command("Foo", "echo 'foo'", {})
  3669. end
  3670. })
  3671. ]], {})
  3672. eq("foo", meths.cmd({ cmd = "Foo" }, { output = true }))
  3673. end)
  3674. it('errors if command is not implemented', function()
  3675. eq("Command not implemented: winpos", pcall_err(meths.cmd, { cmd = "winpos" }, {}))
  3676. end)
  3677. it('works with empty arguments list', function()
  3678. meths.cmd({ cmd = "update" }, {})
  3679. meths.cmd({ cmd = "buffer", count = 0 }, {})
  3680. end)
  3681. it('doesn\'t suppress errors when used in keymapping', function()
  3682. meths.exec_lua([[
  3683. vim.keymap.set("n", "[l",
  3684. function() vim.api.nvim_cmd({ cmd = "echo", args = {"foo"} }, {}) end)
  3685. ]], {})
  3686. feed("[l")
  3687. neq(nil, string.find(eval("v:errmsg"), "E5108:"))
  3688. end)
  3689. it('handles 0 range #19608', function()
  3690. meths.buf_set_lines(0, 0, -1, false, { "aa" })
  3691. meths.cmd({ cmd = 'delete', range = { 0 } }, {})
  3692. command('undo')
  3693. eq({'aa'}, meths.buf_get_lines(0, 0, 1, false))
  3694. assert_alive()
  3695. end)
  3696. it('supports filename expansion', function()
  3697. meths.cmd({ cmd = 'argadd', args = { '%:p:h:t', '%:p:h:t' } }, {})
  3698. local arg = funcs.expand('%:p:h:t')
  3699. eq({ arg, arg }, funcs.argv())
  3700. end)
  3701. it("'make' command works when argument count isn't 1 #19696", function()
  3702. command('set makeprg=echo')
  3703. command('set shellquote=')
  3704. matches('^:!echo ',
  3705. meths.cmd({ cmd = 'make' }, { output = true }))
  3706. assert_alive()
  3707. matches('^:!echo foo bar',
  3708. meths.cmd({ cmd = 'make', args = { 'foo', 'bar' } }, { output = true }))
  3709. assert_alive()
  3710. local arg_pesc = pesc(funcs.expand('%:p:h:t'))
  3711. matches(('^:!echo %s %s'):format(arg_pesc, arg_pesc),
  3712. meths.cmd({ cmd = 'make', args = { '%:p:h:t', '%:p:h:t' } }, { output = true }))
  3713. assert_alive()
  3714. end)
  3715. it('doesn\'t display messages when output=true', function()
  3716. local screen = Screen.new(40, 6)
  3717. screen:attach()
  3718. screen:set_default_attr_ids({
  3719. [0] = {bold=true, foreground=Screen.colors.Blue},
  3720. })
  3721. meths.cmd({cmd = 'echo', args = {[['hello']]}}, {output = true})
  3722. screen:expect{grid=[[
  3723. ^ |
  3724. {0:~ }|
  3725. {0:~ }|
  3726. {0:~ }|
  3727. {0:~ }|
  3728. |
  3729. ]]}
  3730. exec([[
  3731. func Print()
  3732. call nvim_cmd(#{cmd: 'echo', args: ['"hello"']}, #{output: v:true})
  3733. endfunc
  3734. ]])
  3735. feed([[:echon 1 | call Print() | echon 5<CR>]])
  3736. screen:expect{grid=[[
  3737. ^ |
  3738. {0:~ }|
  3739. {0:~ }|
  3740. {0:~ }|
  3741. {0:~ }|
  3742. 15 |
  3743. ]]}
  3744. end)
  3745. it('works with non-String args', function()
  3746. eq('2', meths.cmd({cmd = 'echo', args = {2}}, {output = true}))
  3747. eq('1', meths.cmd({cmd = 'echo', args = {true}}, {output = true}))
  3748. end)
  3749. describe('first argument as count', function()
  3750. before_each(clear)
  3751. it('works', function()
  3752. command('vsplit | enew')
  3753. meths.cmd({cmd = 'bdelete', args = {meths.get_current_buf()}}, {})
  3754. eq(1, meths.get_current_buf().id)
  3755. end)
  3756. it('works with :sleep using milliseconds', function()
  3757. local start = luv.now()
  3758. meths.cmd({cmd = 'sleep', args = {'100m'}}, {})
  3759. ok(luv.now() - start <= 300)
  3760. end)
  3761. end)
  3762. end)
  3763. end)