tui_spec.lua 124 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432
  1. -- TUI acceptance tests.
  2. -- Uses :terminal as a way to send keys and assert screen state.
  3. --
  4. -- "bracketed paste" terminal feature:
  5. -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
  6. local t = require('test.testutil')
  7. local n = require('test.functional.testnvim')()
  8. local Screen = require('test.functional.ui.screen')
  9. local tt = require('test.functional.terminal.testutil')
  10. local eq = t.eq
  11. local feed_data = tt.feed_data
  12. local clear = n.clear
  13. local command = n.command
  14. local dedent = t.dedent
  15. local exec = n.exec
  16. local exec_lua = n.exec_lua
  17. local testprg = n.testprg
  18. local retry = t.retry
  19. local nvim_prog = n.nvim_prog
  20. local nvim_set = n.nvim_set
  21. local ok = t.ok
  22. local read_file = t.read_file
  23. local fn = n.fn
  24. local api = n.api
  25. local is_ci = t.is_ci
  26. local is_os = t.is_os
  27. local new_pipename = n.new_pipename
  28. local spawn_argv = n.spawn_argv
  29. local set_session = n.set_session
  30. local write_file = t.write_file
  31. local eval = n.eval
  32. local assert_log = t.assert_log
  33. local testlog = 'Xtest-tui-log'
  34. if t.skip(is_os('win')) then
  35. return
  36. end
  37. describe('TUI', function()
  38. local screen
  39. local child_session
  40. local child_exec_lua
  41. before_each(function()
  42. clear()
  43. local child_server = new_pipename()
  44. screen = tt.setup_child_nvim({
  45. '--listen',
  46. child_server,
  47. '-u',
  48. 'NONE',
  49. '-i',
  50. 'NONE',
  51. '--cmd',
  52. nvim_set .. ' notermguicolors laststatus=2 background=dark',
  53. '--cmd',
  54. 'colorscheme vim',
  55. })
  56. screen:expect([[
  57. {1: } |
  58. {4:~ }|*3
  59. {5:[No Name] }|
  60. |
  61. {3:-- TERMINAL --} |
  62. ]])
  63. child_session = n.connect(child_server)
  64. child_exec_lua = tt.make_lua_executor(child_session)
  65. end)
  66. -- Wait for mode in the child Nvim (avoid "typeahead race" #10826).
  67. local function wait_for_mode(mode)
  68. retry(nil, nil, function()
  69. local _, m = child_session:request('nvim_get_mode')
  70. eq(mode, m.mode)
  71. end)
  72. end
  73. -- Assert buffer contents in the child Nvim.
  74. local function expect_child_buf_lines(expected)
  75. assert(type({}) == type(expected))
  76. retry(nil, nil, function()
  77. local _, buflines = child_session:request('nvim_buf_get_lines', 0, 0, -1, false)
  78. eq(expected, buflines)
  79. end)
  80. end
  81. it('rapid resize #7572 #7628', function()
  82. -- Need buffer rows to provoke the behavior.
  83. feed_data(':edit test/functional/fixtures/bigfile.txt\n')
  84. screen:expect([[
  85. {1:0}000;<control>;Cc;0;BN;;;;;N;NULL;;;; |
  86. 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
  87. 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
  88. 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
  89. {5:test/functional/fixtures/bigfile.txt }|
  90. :edit test/functional/fixtures/bigfile.txt |
  91. {3:-- TERMINAL --} |
  92. ]])
  93. command('call jobresize(b:terminal_job_id, 58, 9)')
  94. command('call jobresize(b:terminal_job_id, 62, 13)')
  95. command('call jobresize(b:terminal_job_id, 100, 42)')
  96. command('call jobresize(b:terminal_job_id, 37, 1000)')
  97. -- Resize to <5 columns.
  98. screen:try_resize(4, 44)
  99. command('call jobresize(b:terminal_job_id, 4, 1000)')
  100. -- Resize to 1 row, then to 1 column, then increase rows to 4.
  101. screen:try_resize(44, 1)
  102. command('call jobresize(b:terminal_job_id, 44, 1)')
  103. screen:try_resize(1, 1)
  104. command('call jobresize(b:terminal_job_id, 1, 1)')
  105. screen:try_resize(1, 4)
  106. command('call jobresize(b:terminal_job_id, 1, 4)')
  107. screen:try_resize(57, 17)
  108. command('call jobresize(b:terminal_job_id, 57, 17)')
  109. retry(nil, nil, function()
  110. eq({ true, 57 }, { child_session:request('nvim_win_get_width', 0) })
  111. end)
  112. end)
  113. it('accepts resize while pager is active', function()
  114. child_session:request(
  115. 'nvim_exec2',
  116. [[
  117. set more
  118. func! ManyErr()
  119. for i in range(20)
  120. echoerr "FAIL ".i
  121. endfor
  122. endfunc
  123. ]],
  124. {}
  125. )
  126. feed_data(':call ManyErr()\r')
  127. screen:expect {
  128. grid = [[
  129. {8:Error detected while processing function ManyErr:} |
  130. {11:line 2:} |
  131. {8:FAIL 0} |
  132. {8:FAIL 1} |
  133. {8:FAIL 2} |
  134. {10:-- More --}{1: } |
  135. {3:-- TERMINAL --} |
  136. ]],
  137. }
  138. screen:try_resize(50, 10)
  139. screen:expect {
  140. grid = [[
  141. :call ManyErr() |
  142. {8:Error detected while processing function ManyErr:} |
  143. {11:line 2:} |
  144. {8:FAIL 0} |
  145. {8:FAIL 1} |
  146. {8:FAIL 2} |
  147. |*2
  148. {10:-- More --}{1: } |
  149. {3:-- TERMINAL --} |
  150. ]],
  151. }
  152. feed_data('j')
  153. screen:expect {
  154. grid = [[
  155. {8:Error detected while processing function ManyErr:} |
  156. {11:line 2:} |
  157. {8:FAIL 0} |
  158. {8:FAIL 1} |
  159. {8:FAIL 2} |
  160. {8:FAIL 3} |
  161. {8:FAIL 4} |
  162. {8:FAIL 5} |
  163. {10:-- More --}{1: } |
  164. {3:-- TERMINAL --} |
  165. ]],
  166. }
  167. screen:try_resize(50, 7)
  168. screen:expect {
  169. grid = [[
  170. {8:FAIL 1} |
  171. {8:FAIL 2} |
  172. {8:FAIL 3} |
  173. {8:FAIL 4} |
  174. {8:FAIL 5} |
  175. {10:-- More --}{1: } |
  176. {3:-- TERMINAL --} |
  177. ]],
  178. }
  179. screen:try_resize(50, 5)
  180. screen:expect {
  181. grid = [[
  182. {8:FAIL 3} |
  183. {8:FAIL 4} |
  184. {8:FAIL 5} |
  185. {10:-- More --}{1: } |
  186. {3:-- TERMINAL --} |
  187. ]],
  188. }
  189. feed_data('g')
  190. screen:expect {
  191. grid = [[
  192. :call ManyErr() |
  193. {8:Error detected while processing function ManyErr:} |
  194. {11:line 2:} |
  195. {10:-- More --}{1: } |
  196. {3:-- TERMINAL --} |
  197. ]],
  198. }
  199. screen:try_resize(50, 10)
  200. screen:expect {
  201. grid = [[
  202. :call ManyErr() |
  203. {8:Error detected while processing function ManyErr:} |
  204. {11:line 2:} |
  205. {8:FAIL 0} |
  206. {8:FAIL 1} |
  207. {8:FAIL 2} |
  208. {8:FAIL 3} |
  209. {8:FAIL 4} |
  210. {10:-- More --}{1: } |
  211. {3:-- TERMINAL --} |
  212. ]],
  213. }
  214. feed_data('\003')
  215. screen:expect {
  216. grid = [[
  217. {1: } |
  218. {4:~ }|*6
  219. {5:[No Name] }|
  220. |
  221. {3:-- TERMINAL --} |
  222. ]],
  223. }
  224. end)
  225. it('accepts basic utf-8 input', function()
  226. feed_data('iabc\ntest1\ntest2')
  227. screen:expect([[
  228. abc |
  229. test1 |
  230. test2{1: } |
  231. {4:~ }|
  232. {5:[No Name] [+] }|
  233. {3:-- INSERT --} |
  234. {3:-- TERMINAL --} |
  235. ]])
  236. feed_data('\027')
  237. screen:expect([[
  238. abc |
  239. test1 |
  240. test{1:2} |
  241. {4:~ }|
  242. {5:[No Name] [+] }|
  243. |
  244. {3:-- TERMINAL --} |
  245. ]])
  246. end)
  247. it('interprets leading <Esc> byte as ALT modifier in normal-mode', function()
  248. local keys = 'dfghjkl'
  249. for c in keys:gmatch('.') do
  250. feed_data(':nnoremap <a-' .. c .. '> ialt-' .. c .. '<cr><esc>\r')
  251. feed_data('\027' .. c)
  252. end
  253. screen:expect([[
  254. alt-j |
  255. alt-k |
  256. alt-l |
  257. {1: } |
  258. {5:[No Name] [+] }|
  259. |
  260. {3:-- TERMINAL --} |
  261. ]])
  262. feed_data('gg')
  263. screen:expect([[
  264. {1:a}lt-d |
  265. alt-f |
  266. alt-g |
  267. alt-h |
  268. {5:[No Name] [+] }|
  269. |
  270. {3:-- TERMINAL --} |
  271. ]])
  272. end)
  273. it('interprets ESC+key as ALT chord in i_CTRL-V', function()
  274. -- Vim represents ALT/META by setting the "high bit" of the modified key:
  275. -- ALT+j inserts "ê". Nvim does not (#3982).
  276. feed_data('i\022\027j')
  277. screen:expect([[
  278. <M-j>{1: } |
  279. {4:~ }|*3
  280. {5:[No Name] [+] }|
  281. {3:-- INSERT --} |
  282. {3:-- TERMINAL --} |
  283. ]])
  284. end)
  285. it('interprets <Esc>[27u as <Esc>', function()
  286. child_session:request(
  287. 'nvim_exec2',
  288. [[
  289. nnoremap <M-;> <Nop>
  290. nnoremap <Esc> AESC<Esc>
  291. nnoremap ; Asemicolon<Esc>
  292. ]],
  293. {}
  294. )
  295. feed_data('\027[27u;')
  296. screen:expect([[
  297. ESCsemicolo{1:n} |
  298. {4:~ }|*3
  299. {5:[No Name] [+] }|
  300. |
  301. {3:-- TERMINAL --} |
  302. ]])
  303. -- <Esc>; should be recognized as <M-;> when <M-;> is mapped
  304. feed_data('\027;')
  305. screen:expect_unchanged()
  306. end)
  307. it('interprets <Esc><Nul> as <M-C-Space> #17198', function()
  308. feed_data('i\022\027\000')
  309. screen:expect([[
  310. <M-C-Space>{1: } |
  311. {4:~ }|*3
  312. {5:[No Name] [+] }|
  313. {3:-- INSERT --} |
  314. {3:-- TERMINAL --} |
  315. ]])
  316. end)
  317. it('accepts ASCII control sequences', function()
  318. feed_data('i')
  319. feed_data('\022\007') -- ctrl+g
  320. feed_data('\022\022') -- ctrl+v
  321. feed_data('\022\013') -- ctrl+m
  322. screen:expect([[
  323. {6:^G^V^M}{1: } |
  324. {4:~ }|*3
  325. {5:[No Name] [+] }|
  326. {3:-- INSERT --} |
  327. {3:-- TERMINAL --} |
  328. ]])
  329. end)
  330. local function test_mouse_wheel(esc)
  331. child_session:request(
  332. 'nvim_exec2',
  333. [[
  334. set number nostartofline nowrap mousescroll=hor:1,ver:1
  335. call setline(1, repeat([join(range(10), '----')], 10))
  336. vsplit
  337. ]],
  338. {}
  339. )
  340. screen:expect([[
  341. {11: 1 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----|
  342. {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----|
  343. {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
  344. {11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----|
  345. {5:[No Name] [+] }{1:[No Name] [+] }|
  346. |
  347. {3:-- TERMINAL --} |
  348. ]])
  349. -- <ScrollWheelDown> in active window
  350. if esc then
  351. feed_data('\027[<65;8;1M')
  352. else
  353. api.nvim_input_mouse('wheel', 'down', '', 0, 0, 7)
  354. end
  355. screen:expect([[
  356. {11: 2 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----|
  357. {11: 3 }0----1----2----3----4│{11: 2 }0----1----2----3----|
  358. {11: 4 }0----1----2----3----4│{11: 3 }0----1----2----3----|
  359. {11: 5 }0----1----2----3----4│{11: 4 }0----1----2----3----|
  360. {5:[No Name] [+] }{1:[No Name] [+] }|
  361. |
  362. {3:-- TERMINAL --} |
  363. ]])
  364. -- <ScrollWheelDown> in inactive window
  365. if esc then
  366. feed_data('\027[<65;48;1M')
  367. else
  368. api.nvim_input_mouse('wheel', 'down', '', 0, 0, 47)
  369. end
  370. screen:expect([[
  371. {11: 2 }{1:0}----1----2----3----4│{11: 2 }0----1----2----3----|
  372. {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
  373. {11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----|
  374. {11: 5 }0----1----2----3----4│{11: 5 }0----1----2----3----|
  375. {5:[No Name] [+] }{1:[No Name] [+] }|
  376. |
  377. {3:-- TERMINAL --} |
  378. ]])
  379. -- <ScrollWheelRight> in active window
  380. if esc then
  381. feed_data('\027[<67;8;1M')
  382. else
  383. api.nvim_input_mouse('wheel', 'right', '', 0, 0, 7)
  384. end
  385. screen:expect([[
  386. {11: 2 }{1:-}---1----2----3----4-│{11: 2 }0----1----2----3----|
  387. {11: 3 }----1----2----3----4-│{11: 3 }0----1----2----3----|
  388. {11: 4 }----1----2----3----4-│{11: 4 }0----1----2----3----|
  389. {11: 5 }----1----2----3----4-│{11: 5 }0----1----2----3----|
  390. {5:[No Name] [+] }{1:[No Name] [+] }|
  391. |
  392. {3:-- TERMINAL --} |
  393. ]])
  394. -- <ScrollWheelRight> in inactive window
  395. if esc then
  396. feed_data('\027[<67;48;1M')
  397. else
  398. api.nvim_input_mouse('wheel', 'right', '', 0, 0, 47)
  399. end
  400. screen:expect([[
  401. {11: 2 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4|
  402. {11: 3 }----1----2----3----4-│{11: 3 }----1----2----3----4|
  403. {11: 4 }----1----2----3----4-│{11: 4 }----1----2----3----4|
  404. {11: 5 }----1----2----3----4-│{11: 5 }----1----2----3----4|
  405. {5:[No Name] [+] }{1:[No Name] [+] }|
  406. |
  407. {3:-- TERMINAL --} |
  408. ]])
  409. -- <S-ScrollWheelDown> in active window
  410. if esc then
  411. feed_data('\027[<69;8;1M')
  412. else
  413. api.nvim_input_mouse('wheel', 'down', 'S', 0, 0, 7)
  414. end
  415. screen:expect([[
  416. {11: 5 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4|
  417. {11: 6 }----1----2----3----4-│{11: 3 }----1----2----3----4|
  418. {11: 7 }----1----2----3----4-│{11: 4 }----1----2----3----4|
  419. {11: 8 }----1----2----3----4-│{11: 5 }----1----2----3----4|
  420. {5:[No Name] [+] }{1:[No Name] [+] }|
  421. |
  422. {3:-- TERMINAL --} |
  423. ]])
  424. -- <S-ScrollWheelDown> in inactive window
  425. if esc then
  426. feed_data('\027[<69;48;1M')
  427. else
  428. api.nvim_input_mouse('wheel', 'down', 'S', 0, 0, 47)
  429. end
  430. screen:expect([[
  431. {11: 5 }{1:-}---1----2----3----4-│{11: 5 }----1----2----3----4|
  432. {11: 6 }----1----2----3----4-│{11: 6 }----1----2----3----4|
  433. {11: 7 }----1----2----3----4-│{11: 7 }----1----2----3----4|
  434. {11: 8 }----1----2----3----4-│{11: 8 }----1----2----3----4|
  435. {5:[No Name] [+] }{1:[No Name] [+] }|
  436. |
  437. {3:-- TERMINAL --} |
  438. ]])
  439. -- <S-ScrollWheelRight> in active window
  440. if esc then
  441. feed_data('\027[<71;8;1M')
  442. else
  443. api.nvim_input_mouse('wheel', 'right', 'S', 0, 0, 7)
  444. end
  445. screen:expect([[
  446. {11: 5 }{1:-}---6----7----8----9 │{11: 5 }----1----2----3----4|
  447. {11: 6 }----6----7----8----9 │{11: 6 }----1----2----3----4|
  448. {11: 7 }----6----7----8----9 │{11: 7 }----1----2----3----4|
  449. {11: 8 }----6----7----8----9 │{11: 8 }----1----2----3----4|
  450. {5:[No Name] [+] }{1:[No Name] [+] }|
  451. |
  452. {3:-- TERMINAL --} |
  453. ]])
  454. -- <S-ScrollWheelRight> in inactive window
  455. if esc then
  456. feed_data('\027[<71;48;1M')
  457. else
  458. api.nvim_input_mouse('wheel', 'right', 'S', 0, 0, 47)
  459. end
  460. screen:expect([[
  461. {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----|
  462. {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----|
  463. {11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----|
  464. {11: 8 }----6----7----8----9 │{11: 8 }5----6----7----8----|
  465. {5:[No Name] [+] }{1:[No Name] [+] }|
  466. |
  467. {3:-- TERMINAL --} |
  468. ]])
  469. -- <ScrollWheelUp> in active window
  470. if esc then
  471. feed_data('\027[<64;8;1M')
  472. else
  473. api.nvim_input_mouse('wheel', 'up', '', 0, 0, 7)
  474. end
  475. screen:expect([[
  476. {11: 4 }----6----7----8----9 │{11: 5 }5----6----7----8----|
  477. {11: 5 }{1:-}---6----7----8----9 │{11: 6 }5----6----7----8----|
  478. {11: 6 }----6----7----8----9 │{11: 7 }5----6----7----8----|
  479. {11: 7 }----6----7----8----9 │{11: 8 }5----6----7----8----|
  480. {5:[No Name] [+] }{1:[No Name] [+] }|
  481. |
  482. {3:-- TERMINAL --} |
  483. ]])
  484. -- <ScrollWheelUp> in inactive window
  485. if esc then
  486. feed_data('\027[<64;48;1M')
  487. else
  488. api.nvim_input_mouse('wheel', 'up', '', 0, 0, 47)
  489. end
  490. screen:expect([[
  491. {11: 4 }----6----7----8----9 │{11: 4 }5----6----7----8----|
  492. {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----|
  493. {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----|
  494. {11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----|
  495. {5:[No Name] [+] }{1:[No Name] [+] }|
  496. |
  497. {3:-- TERMINAL --} |
  498. ]])
  499. -- <ScrollWheelLeft> in active window
  500. if esc then
  501. feed_data('\027[<66;8;1M')
  502. else
  503. api.nvim_input_mouse('wheel', 'left', '', 0, 0, 7)
  504. end
  505. screen:expect([[
  506. {11: 4 }5----6----7----8----9│{11: 4 }5----6----7----8----|
  507. {11: 5 }5{1:-}---6----7----8----9│{11: 5 }5----6----7----8----|
  508. {11: 6 }5----6----7----8----9│{11: 6 }5----6----7----8----|
  509. {11: 7 }5----6----7----8----9│{11: 7 }5----6----7----8----|
  510. {5:[No Name] [+] }{1:[No Name] [+] }|
  511. |
  512. {3:-- TERMINAL --} |
  513. ]])
  514. -- <ScrollWheelLeft> in inactive window
  515. if esc then
  516. feed_data('\027[<66;48;1M')
  517. else
  518. api.nvim_input_mouse('wheel', 'left', '', 0, 0, 47)
  519. end
  520. screen:expect([[
  521. {11: 4 }5----6----7----8----9│{11: 4 }-5----6----7----8---|
  522. {11: 5 }5{1:-}---6----7----8----9│{11: 5 }-5----6----7----8---|
  523. {11: 6 }5----6----7----8----9│{11: 6 }-5----6----7----8---|
  524. {11: 7 }5----6----7----8----9│{11: 7 }-5----6----7----8---|
  525. {5:[No Name] [+] }{1:[No Name] [+] }|
  526. |
  527. {3:-- TERMINAL --} |
  528. ]])
  529. -- <S-ScrollWheelUp> in active window
  530. if esc then
  531. feed_data('\027[<68;8;1M')
  532. else
  533. api.nvim_input_mouse('wheel', 'up', 'S', 0, 0, 7)
  534. end
  535. screen:expect([[
  536. {11: 1 }5----6----7----8----9│{11: 4 }-5----6----7----8---|
  537. {11: 2 }5----6----7----8----9│{11: 5 }-5----6----7----8---|
  538. {11: 3 }5----6----7----8----9│{11: 6 }-5----6----7----8---|
  539. {11: 4 }5{1:-}---6----7----8----9│{11: 7 }-5----6----7----8---|
  540. {5:[No Name] [+] }{1:[No Name] [+] }|
  541. |
  542. {3:-- TERMINAL --} |
  543. ]])
  544. -- <S-ScrollWheelUp> in inactive window
  545. if esc then
  546. feed_data('\027[<68;48;1M')
  547. else
  548. api.nvim_input_mouse('wheel', 'up', 'S', 0, 0, 47)
  549. end
  550. screen:expect([[
  551. {11: 1 }5----6----7----8----9│{11: 1 }-5----6----7----8---|
  552. {11: 2 }5----6----7----8----9│{11: 2 }-5----6----7----8---|
  553. {11: 3 }5----6----7----8----9│{11: 3 }-5----6----7----8---|
  554. {11: 4 }5{1:-}---6----7----8----9│{11: 4 }-5----6----7----8---|
  555. {5:[No Name] [+] }{1:[No Name] [+] }|
  556. |
  557. {3:-- TERMINAL --} |
  558. ]])
  559. -- <S-ScrollWheelLeft> in active window
  560. if esc then
  561. feed_data('\027[<70;8;1M')
  562. else
  563. api.nvim_input_mouse('wheel', 'left', 'S', 0, 0, 7)
  564. end
  565. screen:expect([[
  566. {11: 1 }0----1----2----3----4│{11: 1 }-5----6----7----8---|
  567. {11: 2 }0----1----2----3----4│{11: 2 }-5----6----7----8---|
  568. {11: 3 }0----1----2----3----4│{11: 3 }-5----6----7----8---|
  569. {11: 4 }0----1----2----3----{1:4}│{11: 4 }-5----6----7----8---|
  570. {5:[No Name] [+] }{1:[No Name] [+] }|
  571. |
  572. {3:-- TERMINAL --} |
  573. ]])
  574. -- <S-ScrollWheelLeft> in inactive window
  575. if esc then
  576. feed_data('\027[<70;48;1M')
  577. else
  578. api.nvim_input_mouse('wheel', 'left', 'S', 0, 0, 47)
  579. end
  580. screen:expect([[
  581. {11: 1 }0----1----2----3----4│{11: 1 }0----1----2----3----|
  582. {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----|
  583. {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
  584. {11: 4 }0----1----2----3----{1:4}│{11: 4 }0----1----2----3----|
  585. {5:[No Name] [+] }{1:[No Name] [+] }|
  586. |
  587. {3:-- TERMINAL --} |
  588. ]])
  589. end
  590. describe('accepts mouse wheel events', function()
  591. it('(mouse events sent to host)', function()
  592. test_mouse_wheel(false)
  593. end)
  594. it('(escape sequences sent to child)', function()
  595. test_mouse_wheel(true)
  596. end)
  597. end)
  598. local function test_mouse_popup(esc)
  599. child_session:request(
  600. 'nvim_exec2',
  601. [[
  602. call setline(1, 'popup menu test')
  603. set mouse=a mousemodel=popup
  604. aunmenu PopUp
  605. menu PopUp.foo :let g:menustr = 'foo'<CR>
  606. menu PopUp.bar :let g:menustr = 'bar'<CR>
  607. menu PopUp.baz :let g:menustr = 'baz'<CR>
  608. highlight Pmenu ctermbg=NONE ctermfg=NONE cterm=underline,reverse
  609. highlight PmenuSel ctermbg=NONE ctermfg=NONE cterm=underline,reverse,bold
  610. ]],
  611. {}
  612. )
  613. if esc then
  614. feed_data('\027[<2;5;1M')
  615. else
  616. api.nvim_input_mouse('right', 'press', '', 0, 0, 4)
  617. end
  618. screen:expect([[
  619. {1:p}opup menu test |
  620. {4:~ }{13: foo }{4: }|
  621. {4:~ }{13: bar }{4: }|
  622. {4:~ }{13: baz }{4: }|
  623. {5:[No Name] [+] }|
  624. |
  625. {3:-- TERMINAL --} |
  626. ]])
  627. if esc then
  628. feed_data('\027[<2;5;1m')
  629. else
  630. api.nvim_input_mouse('right', 'release', '', 0, 0, 4)
  631. end
  632. screen:expect_unchanged()
  633. if esc then
  634. feed_data('\027[<64;5;1M')
  635. else
  636. api.nvim_input_mouse('wheel', 'up', '', 0, 0, 4)
  637. end
  638. screen:expect([[
  639. {1:p}opup menu test |
  640. {4:~ }{14: foo }{4: }|
  641. {4:~ }{13: bar }{4: }|
  642. {4:~ }{13: baz }{4: }|
  643. {5:[No Name] [+] }|
  644. |
  645. {3:-- TERMINAL --} |
  646. ]])
  647. if esc then
  648. feed_data('\027[<35;7;4M')
  649. else
  650. api.nvim_input_mouse('move', '', '', 0, 3, 6)
  651. end
  652. screen:expect([[
  653. {1:p}opup menu test |
  654. {4:~ }{13: foo }{4: }|
  655. {4:~ }{13: bar }{4: }|
  656. {4:~ }{14: baz }{4: }|
  657. {5:[No Name] [+] }|
  658. |
  659. {3:-- TERMINAL --} |
  660. ]])
  661. if esc then
  662. feed_data('\027[<65;7;4M')
  663. else
  664. api.nvim_input_mouse('wheel', 'down', '', 0, 3, 6)
  665. end
  666. screen:expect([[
  667. {1:p}opup menu test |
  668. {4:~ }{13: foo }{4: }|
  669. {4:~ }{14: bar }{4: }|
  670. {4:~ }{13: baz }{4: }|
  671. {5:[No Name] [+] }|
  672. |
  673. {3:-- TERMINAL --} |
  674. ]])
  675. if esc then
  676. feed_data('\027[<0;7;3M')
  677. else
  678. api.nvim_input_mouse('left', 'press', '', 0, 2, 6)
  679. end
  680. screen:expect([[
  681. {1:p}opup menu test |
  682. {4:~ }|*3
  683. {5:[No Name] [+] }|
  684. :let g:menustr = 'bar' |
  685. {3:-- TERMINAL --} |
  686. ]])
  687. if esc then
  688. feed_data('\027[<0;7;3m')
  689. else
  690. api.nvim_input_mouse('left', 'release', '', 0, 2, 6)
  691. end
  692. screen:expect_unchanged()
  693. if esc then
  694. feed_data('\027[<2;45;3M')
  695. else
  696. api.nvim_input_mouse('right', 'press', '', 0, 2, 44)
  697. end
  698. screen:expect([[
  699. {1:p}opup menu test |
  700. {4:~ }|*2
  701. {4:~ }{13: foo }{4: }|
  702. {5:[No Name] [+] }{13: bar }{5: }|
  703. :let g:menustr = 'bar' {13: baz } |
  704. {3:-- TERMINAL --} |
  705. ]])
  706. if esc then
  707. feed_data('\027[<34;48;6M')
  708. else
  709. api.nvim_input_mouse('right', 'drag', '', 0, 5, 47)
  710. end
  711. screen:expect([[
  712. {1:p}opup menu test |
  713. {4:~ }|*2
  714. {4:~ }{13: foo }{4: }|
  715. {5:[No Name] [+] }{13: bar }{5: }|
  716. :let g:menustr = 'bar' {14: baz } |
  717. {3:-- TERMINAL --} |
  718. ]])
  719. if esc then
  720. feed_data('\027[<2;48;6m')
  721. else
  722. api.nvim_input_mouse('right', 'release', '', 0, 5, 47)
  723. end
  724. screen:expect([[
  725. {1:p}opup menu test |
  726. {4:~ }|*3
  727. {5:[No Name] [+] }|
  728. :let g:menustr = 'baz' |
  729. {3:-- TERMINAL --} |
  730. ]])
  731. end
  732. describe('mouse events work with right-click menu', function()
  733. it('(mouse events sent to host)', function()
  734. test_mouse_popup(false)
  735. end)
  736. it('(escape sequences sent to child)', function()
  737. test_mouse_popup(true)
  738. end)
  739. end)
  740. it('accepts keypad keys from kitty keyboard protocol #19180', function()
  741. feed_data('i')
  742. feed_data(fn.nr2char(57399)) -- KP_0
  743. feed_data(fn.nr2char(57400)) -- KP_1
  744. feed_data(fn.nr2char(57401)) -- KP_2
  745. feed_data(fn.nr2char(57402)) -- KP_3
  746. feed_data(fn.nr2char(57403)) -- KP_4
  747. feed_data(fn.nr2char(57404)) -- KP_5
  748. feed_data(fn.nr2char(57405)) -- KP_6
  749. feed_data(fn.nr2char(57406)) -- KP_7
  750. feed_data(fn.nr2char(57407)) -- KP_8
  751. feed_data(fn.nr2char(57408)) -- KP_9
  752. feed_data(fn.nr2char(57409)) -- KP_DECIMAL
  753. feed_data(fn.nr2char(57410)) -- KP_DIVIDE
  754. feed_data(fn.nr2char(57411)) -- KP_MULTIPLY
  755. feed_data(fn.nr2char(57412)) -- KP_SUBTRACT
  756. feed_data(fn.nr2char(57413)) -- KP_ADD
  757. feed_data(fn.nr2char(57414)) -- KP_ENTER
  758. feed_data(fn.nr2char(57415)) -- KP_EQUAL
  759. screen:expect([[
  760. 0123456789./*-+ |
  761. ={1: } |
  762. {4:~ }|*2
  763. {5:[No Name] [+] }|
  764. {3:-- INSERT --} |
  765. {3:-- TERMINAL --} |
  766. ]])
  767. feed_data(fn.nr2char(57417)) -- KP_LEFT
  768. screen:expect([[
  769. 0123456789./*-+ |
  770. {1:=} |
  771. {4:~ }|*2
  772. {5:[No Name] [+] }|
  773. {3:-- INSERT --} |
  774. {3:-- TERMINAL --} |
  775. ]])
  776. feed_data(fn.nr2char(57418)) -- KP_RIGHT
  777. screen:expect([[
  778. 0123456789./*-+ |
  779. ={1: } |
  780. {4:~ }|*2
  781. {5:[No Name] [+] }|
  782. {3:-- INSERT --} |
  783. {3:-- TERMINAL --} |
  784. ]])
  785. feed_data(fn.nr2char(57419)) -- KP_UP
  786. screen:expect([[
  787. 0{1:1}23456789./*-+ |
  788. = |
  789. {4:~ }|*2
  790. {5:[No Name] [+] }|
  791. {3:-- INSERT --} |
  792. {3:-- TERMINAL --} |
  793. ]])
  794. feed_data(fn.nr2char(57420)) -- KP_DOWN
  795. screen:expect([[
  796. 0123456789./*-+ |
  797. ={1: } |
  798. {4:~ }|*2
  799. {5:[No Name] [+] }|
  800. {3:-- INSERT --} |
  801. {3:-- TERMINAL --} |
  802. ]])
  803. feed_data(fn.nr2char(57425)) -- KP_INSERT
  804. screen:expect([[
  805. 0123456789./*-+ |
  806. ={1: } |
  807. {4:~ }|*2
  808. {5:[No Name] [+] }|
  809. {3:-- REPLACE --} |
  810. {3:-- TERMINAL --} |
  811. ]])
  812. feed_data('\027[27u') -- ESC
  813. screen:expect([[
  814. 0123456789./*-+ |
  815. {1:=} |
  816. {4:~ }|*2
  817. {5:[No Name] [+] }|
  818. |
  819. {3:-- TERMINAL --} |
  820. ]])
  821. feed_data('\027[57417;5u') -- CTRL + KP_LEFT
  822. screen:expect([[
  823. {1:0}123456789./*-+ |
  824. = |
  825. {4:~ }|*2
  826. {5:[No Name] [+] }|
  827. |
  828. {3:-- TERMINAL --} |
  829. ]])
  830. feed_data('\027[57418;2u') -- SHIFT + KP_RIGHT
  831. screen:expect([[
  832. 0123456789{1:.}/*-+ |
  833. = |
  834. {4:~ }|*2
  835. {5:[No Name] [+] }|
  836. |
  837. {3:-- TERMINAL --} |
  838. ]])
  839. feed_data(fn.nr2char(57426)) -- KP_DELETE
  840. screen:expect([[
  841. 0123456789{1:/}*-+ |
  842. = |
  843. {4:~ }|*2
  844. {5:[No Name] [+] }|
  845. |
  846. {3:-- TERMINAL --} |
  847. ]])
  848. feed_data(fn.nr2char(57423)) -- KP_HOME
  849. screen:expect([[
  850. {1:0}123456789/*-+ |
  851. = |
  852. {4:~ }|*2
  853. {5:[No Name] [+] }|
  854. |
  855. {3:-- TERMINAL --} |
  856. ]])
  857. feed_data(fn.nr2char(57424)) -- KP_END
  858. screen:expect([[
  859. 0123456789/*-{1:+} |
  860. = |
  861. {4:~ }|*2
  862. {5:[No Name] [+] }|
  863. |
  864. {3:-- TERMINAL --} |
  865. ]])
  866. child_session:request(
  867. 'nvim_exec2',
  868. [[
  869. tab split
  870. tabnew
  871. highlight Tabline ctermbg=NONE ctermfg=NONE cterm=underline
  872. ]],
  873. {}
  874. )
  875. screen:expect([[
  876. {12: + [No Name] + [No Name] }{3: [No Name] }{1: }{12:X}|
  877. {1: } |
  878. {4:~ }|*2
  879. {5:[No Name] }|
  880. |
  881. {3:-- TERMINAL --} |
  882. ]])
  883. feed_data('\027[57421;5u') -- CTRL + KP_PAGE_UP
  884. screen:expect([[
  885. {12: + [No Name] }{3: + [No Name] }{12: [No Name] }{1: }{12:X}|
  886. 0123456789/*-{1:+} |
  887. = |
  888. {4:~ }|
  889. {5:[No Name] [+] }|
  890. |
  891. {3:-- TERMINAL --} |
  892. ]])
  893. feed_data('\027[57422;5u') -- CTRL + KP_PAGE_DOWN
  894. screen:expect([[
  895. {12: + [No Name] + [No Name] }{3: [No Name] }{1: }{12:X}|
  896. {1: } |
  897. {4:~ }|*2
  898. {5:[No Name] }|
  899. |
  900. {3:-- TERMINAL --} |
  901. ]])
  902. end)
  903. it('supports Super and Meta modifiers', function()
  904. feed_data('i')
  905. feed_data('\022\027[106;9u') -- Super + j
  906. feed_data('\022\027[107;33u') -- Meta + k
  907. feed_data('\022\027[13;41u') -- Super + Meta + Enter
  908. feed_data('\022\027[127;48u') -- Shift + Alt + Ctrl + Super + Meta + Backspace
  909. feed_data('\n')
  910. feed_data('\022\027[57376;9u') -- Super + F13
  911. feed_data('\022\027[57377;33u') -- Meta + F14
  912. feed_data('\022\027[57378;41u') -- Super + Meta + F15
  913. feed_data('\022\027[57379;48u') -- Shift + Alt + Ctrl + Super + Meta + F16
  914. screen:expect([[
  915. <D-j><T-k><T-D-CR><M-T-C-S-D-BS> |
  916. <D-F13><T-F14><T-D-F15><M-T-C-S-D-F16>{1: } |
  917. {4:~ }|*2
  918. {5:[No Name] [+] }|
  919. {3:-- INSERT --} |
  920. {3:-- TERMINAL --} |
  921. ]])
  922. end)
  923. it('paste: Insert mode', function()
  924. -- "bracketed paste"
  925. feed_data('i""\027i\027[200~')
  926. screen:expect([[
  927. "{1:"} |
  928. {4:~ }|*3
  929. {5:[No Name] [+] }|
  930. {3:-- INSERT --} |
  931. {3:-- TERMINAL --} |
  932. ]])
  933. feed_data('pasted from terminal')
  934. expect_child_buf_lines({ '"pasted from terminal"' })
  935. screen:expect([[
  936. "pasted from terminal{1:"} |
  937. {4:~ }|*3
  938. {5:[No Name] [+] }|
  939. {3:-- INSERT --} |
  940. {3:-- TERMINAL --} |
  941. ]])
  942. feed_data('\027[201~') -- End paste.
  943. screen:expect_unchanged()
  944. feed_data('\027[27u') -- ESC: go to Normal mode.
  945. wait_for_mode('n')
  946. screen:expect([[
  947. "pasted from termina{1:l}" |
  948. {4:~ }|*3
  949. {5:[No Name] [+] }|
  950. |
  951. {3:-- TERMINAL --} |
  952. ]])
  953. -- Dot-repeat/redo.
  954. feed_data('2.')
  955. expect_child_buf_lines({ '"pasted from terminapasted from terminalpasted from terminall"' })
  956. screen:expect([[
  957. "pasted from terminapasted from terminalpasted fro|
  958. m termina{1:l}l" |
  959. {4:~ }|*2
  960. {5:[No Name] [+] }|
  961. |
  962. {3:-- TERMINAL --} |
  963. ]])
  964. -- Undo.
  965. feed_data('u')
  966. expect_child_buf_lines({ '"pasted from terminal"' })
  967. feed_data('u')
  968. expect_child_buf_lines({ '""' })
  969. feed_data('u')
  970. expect_child_buf_lines({ '' })
  971. end)
  972. it('paste: select-mode', function()
  973. feed_data('ithis is line 1\nthis is line 2\nline 3 is here\n\027')
  974. wait_for_mode('n')
  975. screen:expect([[
  976. this is line 1 |
  977. this is line 2 |
  978. line 3 is here |
  979. {1: } |
  980. {5:[No Name] [+] }|
  981. |
  982. {3:-- TERMINAL --} |
  983. ]])
  984. -- Select-mode. Use <C-n> to move down.
  985. feed_data('gg04lgh\14\14')
  986. screen:expect([[
  987. this{16: is line 1} |
  988. {16:this is line 2} |
  989. {16:line}{1: }3 is here |
  990. |
  991. {5:[No Name] [+] }|
  992. {3:-- SELECT --} |
  993. {3:-- TERMINAL --} |
  994. ]])
  995. feed_data('\027[200~')
  996. feed_data('just paste it™')
  997. feed_data('\027[201~')
  998. screen:expect([[
  999. thisjust paste it{1:™}3 is here |
  1000. |
  1001. {4:~ }|*2
  1002. {5:[No Name] [+] }|
  1003. |
  1004. {3:-- TERMINAL --} |
  1005. ]])
  1006. -- Undo.
  1007. feed_data('u')
  1008. expect_child_buf_lines {
  1009. 'this is line 1',
  1010. 'this is line 2',
  1011. 'line 3 is here',
  1012. '',
  1013. }
  1014. -- Redo.
  1015. feed_data('\18') -- <C-r>
  1016. expect_child_buf_lines {
  1017. 'thisjust paste it™3 is here',
  1018. '',
  1019. }
  1020. end)
  1021. it('paste: terminal mode', function()
  1022. if is_ci('github') then
  1023. pending('tty-test complains about not owning the terminal -- actions/runner#241')
  1024. end
  1025. child_exec_lua('vim.o.statusline="^^^^^^^"')
  1026. child_exec_lua('vim.cmd.terminal(...)', testprg('tty-test'))
  1027. feed_data('i')
  1028. screen:expect([[
  1029. tty ready |
  1030. {1: } |
  1031. |*2
  1032. {5:^^^^^^^ }|
  1033. {3:-- TERMINAL --} |*2
  1034. ]])
  1035. feed_data('\027[200~')
  1036. feed_data('hallo')
  1037. feed_data('\027[201~')
  1038. screen:expect([[
  1039. tty ready |
  1040. hallo{1: } |
  1041. |*2
  1042. {5:^^^^^^^ }|
  1043. {3:-- TERMINAL --} |*2
  1044. ]])
  1045. end)
  1046. it('paste: normal-mode (+CRLF #10872)', function()
  1047. feed_data(':set ruler | echo')
  1048. wait_for_mode('c')
  1049. feed_data('\n')
  1050. wait_for_mode('n')
  1051. local expected_lf = { 'line 1', 'ESC:\027 / CR: \rx' }
  1052. local expected_crlf = { 'line 1', 'ESC:\027 / CR: ', 'x' }
  1053. local expected_grid1 = [[
  1054. line 1 |
  1055. ESC:{6:^[} / CR: |
  1056. {1:x} |
  1057. {4:~ }|
  1058. {5:[No Name] [+] 3,1 All}|
  1059. |
  1060. {3:-- TERMINAL --} |
  1061. ]]
  1062. -- "bracketed paste"
  1063. feed_data('\027[200~' .. table.concat(expected_lf, '\n') .. '\027[201~')
  1064. screen:expect(expected_grid1)
  1065. -- Dot-repeat/redo.
  1066. feed_data('.')
  1067. screen:expect([[
  1068. ESC:{6:^[} / CR: |
  1069. xline 1 |
  1070. ESC:{6:^[} / CR: |
  1071. {1:x} |
  1072. {5:[No Name] [+] 5,1 Bot}|
  1073. |
  1074. {3:-- TERMINAL --} |
  1075. ]])
  1076. -- Undo.
  1077. feed_data('u')
  1078. expect_child_buf_lines(expected_crlf)
  1079. feed_data('u')
  1080. expect_child_buf_lines({ '' })
  1081. feed_data(':echo')
  1082. wait_for_mode('c')
  1083. feed_data('\n')
  1084. wait_for_mode('n')
  1085. -- CRLF input
  1086. feed_data('\027[200~' .. table.concat(expected_lf, '\r\n') .. '\027[201~')
  1087. screen:expect(expected_grid1)
  1088. expect_child_buf_lines(expected_crlf)
  1089. end)
  1090. it('paste: cmdline-mode inserts 1 line', function()
  1091. feed_data('ifoo\n') -- Insert some text (for dot-repeat later).
  1092. feed_data('\027:""') -- Enter Cmdline-mode.
  1093. feed_data('\027[D') -- <Left> to place cursor between quotes.
  1094. wait_for_mode('c')
  1095. screen:expect([[
  1096. foo |
  1097. |
  1098. {4:~ }|*2
  1099. {5:[No Name] [+] }|
  1100. :"{1:"} |
  1101. {3:-- TERMINAL --} |
  1102. ]])
  1103. -- "bracketed paste"
  1104. feed_data('\027[200~line 1\nline 2\n')
  1105. wait_for_mode('c')
  1106. feed_data('line 3\nline 4\n\027[201~')
  1107. wait_for_mode('c')
  1108. screen:expect([[
  1109. foo |
  1110. |
  1111. {4:~ }|*2
  1112. {5:[No Name] [+] }|
  1113. :"line 1{1:"} |
  1114. {3:-- TERMINAL --} |
  1115. ]])
  1116. -- Dot-repeat/redo.
  1117. feed_data('\027[27u')
  1118. wait_for_mode('n')
  1119. feed_data('.')
  1120. screen:expect([[
  1121. foo |*2
  1122. {1: } |
  1123. {4:~ }|
  1124. {5:[No Name] [+] }|
  1125. |
  1126. {3:-- TERMINAL --} |
  1127. ]])
  1128. end)
  1129. it('paste: cmdline-mode collects chunks of unfinished line', function()
  1130. local function expect_cmdline(expected)
  1131. retry(nil, nil, function()
  1132. local _, cmdline = child_session:request('nvim_call_function', 'getcmdline', {})
  1133. eq(expected, cmdline)
  1134. local _, pos = child_session:request('nvim_call_function', 'getcmdpos', {})
  1135. eq(#expected, pos) -- Cursor is just before the last char.
  1136. end)
  1137. end
  1138. feed_data('\027:""') -- Enter Cmdline-mode.
  1139. feed_data('\027[D') -- <Left> to place cursor between quotes.
  1140. expect_cmdline('""')
  1141. feed_data('\027[200~stuff 1 ')
  1142. expect_cmdline('"stuff 1 "')
  1143. -- Discards everything after the first line.
  1144. feed_data('more\nstuff 2\nstuff 3\n')
  1145. expect_cmdline('"stuff 1 more"')
  1146. feed_data('stuff 3')
  1147. expect_cmdline('"stuff 1 more"')
  1148. -- End the paste sequence.
  1149. feed_data('\027[201~')
  1150. expect_cmdline('"stuff 1 more"')
  1151. feed_data(' typed')
  1152. expect_cmdline('"stuff 1 more typed"')
  1153. end)
  1154. it('paste: recovers from vim.paste() failure', function()
  1155. child_session:request(
  1156. 'nvim_exec_lua',
  1157. [[
  1158. _G.save_paste_fn = vim.paste
  1159. -- Stack traces for this test are non-deterministic, so disable them
  1160. _G.debug.traceback = function(msg) return msg end
  1161. vim.paste = function(lines, phase) error("fake fail") end
  1162. ]],
  1163. {}
  1164. )
  1165. -- Prepare something for dot-repeat/redo.
  1166. feed_data('ifoo\n\027[27u')
  1167. wait_for_mode('n')
  1168. screen:expect([[
  1169. foo |
  1170. {1: } |
  1171. {4:~ }|*2
  1172. {5:[No Name] [+] }|
  1173. |
  1174. {3:-- TERMINAL --} |
  1175. ]])
  1176. -- Start pasting...
  1177. feed_data('\027[200~line 1\nline 2\n')
  1178. screen:expect([[
  1179. foo |
  1180. |
  1181. {5: }|
  1182. {8:paste: Error executing lua: [string "<nvim>"]:4: f}|
  1183. {8:ake fail} |
  1184. {10:Press ENTER or type command to continue}{1: } |
  1185. {3:-- TERMINAL --} |
  1186. ]])
  1187. -- Remaining chunks are discarded after vim.paste() failure.
  1188. feed_data('line 3\nline 4\n')
  1189. feed_data('line 5\nline 6\n')
  1190. feed_data('line 7\nline 8\n')
  1191. -- Stop paste.
  1192. feed_data('\027[201~')
  1193. screen:expect_unchanged()
  1194. feed_data('\n') -- <CR> to dismiss hit-enter prompt
  1195. expect_child_buf_lines({ 'foo', '' })
  1196. -- Dot-repeat/redo is not modified by failed paste.
  1197. feed_data('.')
  1198. screen:expect([[
  1199. foo |*2
  1200. {1: } |
  1201. {4:~ }|
  1202. {5:[No Name] [+] }|
  1203. |
  1204. {3:-- TERMINAL --} |
  1205. ]])
  1206. -- Editor should still work after failed/drained paste.
  1207. feed_data('ityped input...\027[27u')
  1208. screen:expect([[
  1209. foo |*2
  1210. typed input..{1:.} |
  1211. {4:~ }|
  1212. {5:[No Name] [+] }|
  1213. |
  1214. {3:-- TERMINAL --} |
  1215. ]])
  1216. -- Paste works if vim.paste() succeeds.
  1217. child_session:request('nvim_exec_lua', [[vim.paste = _G.save_paste_fn]], {})
  1218. feed_data('\027[200~line A\nline B\n\027[201~')
  1219. screen:expect([[
  1220. foo |
  1221. typed input...line A |
  1222. line B |
  1223. {1: } |
  1224. {5:[No Name] [+] }|
  1225. |
  1226. {3:-- TERMINAL --} |
  1227. ]])
  1228. end)
  1229. it('paste: vim.paste() cancel (retval=false) #10865', function()
  1230. -- This test only exercises the "cancel" case. Use-case would be "dangling
  1231. -- paste", but that is not implemented yet. #10865
  1232. child_session:request(
  1233. 'nvim_exec_lua',
  1234. [[
  1235. vim.paste = function(lines, phase) return false end
  1236. ]],
  1237. {}
  1238. )
  1239. feed_data('\027[200~line A\nline B\n\027[201~')
  1240. expect_child_buf_lines({ '' })
  1241. feed_data('ifoo\n\027[27u')
  1242. expect_child_buf_lines({ 'foo', '' })
  1243. end)
  1244. it("paste: 'nomodifiable' buffer", function()
  1245. child_session:request('nvim_command', 'set nomodifiable')
  1246. child_session:request(
  1247. 'nvim_exec_lua',
  1248. [[
  1249. -- Truncate the error message to hide the line number
  1250. _G.debug.traceback = function(msg) return msg:sub(-49) end
  1251. ]],
  1252. {}
  1253. )
  1254. feed_data('\027[200~fail 1\nfail 2\n\027[201~')
  1255. screen:expect([[
  1256. |
  1257. {4:~ }|
  1258. {5: }|
  1259. {8:paste: Error executing lua: Vim:E21: Cannot make c}|
  1260. {8:hanges, 'modifiable' is off} |
  1261. {10:Press ENTER or type command to continue}{1: } |
  1262. {3:-- TERMINAL --} |
  1263. ]])
  1264. feed_data('\n') -- <Enter> to dismiss hit-enter prompt
  1265. child_session:request('nvim_command', 'set modifiable')
  1266. feed_data('\027[200~success 1\nsuccess 2\n\027[201~')
  1267. screen:expect([[
  1268. success 1 |
  1269. success 2 |
  1270. {1: } |
  1271. {4:~ }|
  1272. {5:[No Name] [+] }|
  1273. |
  1274. {3:-- TERMINAL --} |
  1275. ]])
  1276. end)
  1277. it('paste: exactly 64 bytes #10311', function()
  1278. local expected = string.rep('z', 64)
  1279. feed_data('i')
  1280. wait_for_mode('i')
  1281. -- "bracketed paste"
  1282. feed_data('\027[200~' .. expected .. '\027[201~')
  1283. expect_child_buf_lines({ expected })
  1284. feed_data(' end')
  1285. expected = expected .. ' end'
  1286. screen:expect([[
  1287. zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz|
  1288. zzzzzzzzzzzzzz end{1: } |
  1289. {4:~ }|*2
  1290. {5:[No Name] [+] }|
  1291. {3:-- INSERT --} |
  1292. {3:-- TERMINAL --} |
  1293. ]])
  1294. expect_child_buf_lines({ expected })
  1295. end)
  1296. it('paste: less-than sign in cmdline #11088', function()
  1297. local expected = '<'
  1298. feed_data(':')
  1299. wait_for_mode('c')
  1300. -- "bracketed paste"
  1301. feed_data('\027[200~' .. expected .. '\027[201~')
  1302. screen:expect([[
  1303. |
  1304. {4:~ }|*3
  1305. {5:[No Name] }|
  1306. :<{1: } |
  1307. {3:-- TERMINAL --} |
  1308. ]])
  1309. end)
  1310. it('paste: big burst of input', function()
  1311. feed_data(':set ruler\n')
  1312. local q = {}
  1313. for i = 1, 3000 do
  1314. q[i] = 'item ' .. tostring(i)
  1315. end
  1316. feed_data('i')
  1317. wait_for_mode('i')
  1318. -- "bracketed paste"
  1319. feed_data('\027[200~' .. table.concat(q, '\n') .. '\027[201~')
  1320. expect_child_buf_lines(q)
  1321. feed_data(' end')
  1322. screen:expect([[
  1323. item 2997 |
  1324. item 2998 |
  1325. item 2999 |
  1326. item 3000 end{1: } |
  1327. {5:[No Name] [+] 3000,14 Bot}|
  1328. {3:-- INSERT --} |
  1329. {3:-- TERMINAL --} |
  1330. ]])
  1331. feed_data('\027[27u') -- ESC: go to Normal mode.
  1332. wait_for_mode('n')
  1333. -- Dot-repeat/redo.
  1334. feed_data('.')
  1335. screen:expect([[
  1336. item 2997 |
  1337. item 2998 |
  1338. item 2999 |
  1339. item 3000 en{1:d}d |
  1340. {5:[No Name] [+] 5999,13 Bot}|
  1341. |
  1342. {3:-- TERMINAL --} |
  1343. ]])
  1344. end)
  1345. it('paste: forwards spurious "start paste" code', function()
  1346. -- If multiple "start paste" sequences are sent without a corresponding
  1347. -- "stop paste" sequence, only the first occurrence should be consumed.
  1348. feed_data('i')
  1349. wait_for_mode('i')
  1350. -- Send the "start paste" sequence.
  1351. feed_data('\027[200~')
  1352. feed_data('\npasted from terminal (1)\n')
  1353. -- Send spurious "start paste" sequence.
  1354. feed_data('\027[200~')
  1355. feed_data('\n')
  1356. -- Send the "stop paste" sequence.
  1357. feed_data('\027[201~')
  1358. screen:expect([[
  1359. |
  1360. pasted from terminal (1) |
  1361. {6:^[}[200~ |
  1362. {1: } |
  1363. {5:[No Name] [+] }|
  1364. {3:-- INSERT --} |
  1365. {3:-- TERMINAL --} |
  1366. ]])
  1367. end)
  1368. it('paste: ignores spurious "stop paste" code', function()
  1369. -- If "stop paste" sequence is received without a preceding "start paste"
  1370. -- sequence, it should be ignored.
  1371. feed_data('i')
  1372. wait_for_mode('i')
  1373. -- Send "stop paste" sequence.
  1374. feed_data('\027[201~')
  1375. screen:expect([[
  1376. {1: } |
  1377. {4:~ }|*3
  1378. {5:[No Name] }|
  1379. {3:-- INSERT --} |
  1380. {3:-- TERMINAL --} |
  1381. ]])
  1382. end)
  1383. it('paste: split "start paste" code', function()
  1384. feed_data('i')
  1385. wait_for_mode('i')
  1386. -- Send split "start paste" sequence.
  1387. feed_data('\027[2')
  1388. feed_data('00~pasted from terminal\027[201~')
  1389. screen:expect([[
  1390. pasted from terminal{1: } |
  1391. {4:~ }|*3
  1392. {5:[No Name] [+] }|
  1393. {3:-- INSERT --} |
  1394. {3:-- TERMINAL --} |
  1395. ]])
  1396. end)
  1397. it('paste: split "stop paste" code', function()
  1398. feed_data('i')
  1399. wait_for_mode('i')
  1400. -- Send split "stop paste" sequence.
  1401. feed_data('\027[200~pasted from terminal\027[20')
  1402. feed_data('1~')
  1403. screen:expect([[
  1404. pasted from terminal{1: } |
  1405. {4:~ }|*3
  1406. {5:[No Name] [+] }|
  1407. {3:-- INSERT --} |
  1408. {3:-- TERMINAL --} |
  1409. ]])
  1410. end)
  1411. it('paste: streamed paste with isolated "stop paste" code', function()
  1412. child_session:request(
  1413. 'nvim_exec_lua',
  1414. [[
  1415. _G.paste_phases = {}
  1416. vim.paste = (function(overridden)
  1417. return function(lines, phase)
  1418. table.insert(_G.paste_phases, phase)
  1419. overridden(lines, phase)
  1420. end
  1421. end)(vim.paste)
  1422. ]],
  1423. {}
  1424. )
  1425. feed_data('i')
  1426. wait_for_mode('i')
  1427. feed_data('\027[200~pasted') -- phase 1
  1428. screen:expect([[
  1429. pasted{1: } |
  1430. {4:~ }|*3
  1431. {5:[No Name] [+] }|
  1432. {3:-- INSERT --} |
  1433. {3:-- TERMINAL --} |
  1434. ]])
  1435. feed_data(' from terminal') -- phase 2
  1436. screen:expect([[
  1437. pasted from terminal{1: } |
  1438. {4:~ }|*3
  1439. {5:[No Name] [+] }|
  1440. {3:-- INSERT --} |
  1441. {3:-- TERMINAL --} |
  1442. ]])
  1443. -- Send isolated "stop paste" sequence.
  1444. feed_data('\027[201~') -- phase 3
  1445. screen:expect_unchanged()
  1446. local _, rv = child_session:request('nvim_exec_lua', [[return _G.paste_phases]], {})
  1447. -- In rare cases there may be multiple chunks of phase 2 because of timing.
  1448. eq({ 1, 2, 3 }, { rv[1], rv[2], rv[#rv] })
  1449. end)
  1450. it('allows termguicolors to be set at runtime', function()
  1451. screen:set_option('rgb', true)
  1452. screen:set_default_attr_ids({
  1453. [1] = { reverse = true },
  1454. [2] = { foreground = tonumber('0x4040ff'), fg_indexed = true },
  1455. [3] = { bold = true, reverse = true },
  1456. [4] = { bold = true },
  1457. [5] = { reverse = true, foreground = tonumber('0xe0e000'), fg_indexed = true },
  1458. [6] = { foreground = tonumber('0xe0e000'), fg_indexed = true },
  1459. [7] = { reverse = true, foreground = Screen.colors.SeaGreen4 },
  1460. [8] = { foreground = Screen.colors.SeaGreen4 },
  1461. [9] = { bold = true, foreground = Screen.colors.Blue1 },
  1462. [10] = { foreground = Screen.colors.Blue },
  1463. })
  1464. feed_data(':hi SpecialKey ctermfg=3 guifg=SeaGreen\n')
  1465. feed_data('i')
  1466. feed_data('\022\007') -- ctrl+g
  1467. feed_data('\028\014') -- crtl+\ ctrl+N
  1468. feed_data(':set termguicolors?\n')
  1469. screen:expect([[
  1470. {5:^}{6:G} |
  1471. {2:~ }|*3
  1472. {3:[No Name] [+] }|
  1473. notermguicolors |
  1474. {4:-- TERMINAL --} |
  1475. ]])
  1476. feed_data(':set termguicolors\n')
  1477. screen:expect([[
  1478. {7:^}{8:G} |
  1479. {9:~}{10: }|*3
  1480. {3:[No Name] [+] }|
  1481. :set termguicolors |
  1482. {4:-- TERMINAL --} |
  1483. ]])
  1484. feed_data(':set notermguicolors\n')
  1485. screen:expect([[
  1486. {5:^}{6:G} |
  1487. {2:~ }|*3
  1488. {3:[No Name] [+] }|
  1489. :set notermguicolors |
  1490. {4:-- TERMINAL --} |
  1491. ]])
  1492. end)
  1493. it('forwards :term palette colors with termguicolors', function()
  1494. if is_ci('github') then
  1495. pending('tty-test complains about not owning the terminal -- actions/runner#241')
  1496. end
  1497. screen:set_rgb_cterm(true)
  1498. screen:set_default_attr_ids({
  1499. [1] = { { reverse = true }, { reverse = true } },
  1500. [2] = { { bold = true, reverse = true }, { bold = true, reverse = true } },
  1501. [3] = { { bold = true }, { bold = true } },
  1502. [4] = { { fg_indexed = true, foreground = tonumber('0xe0e000') }, { foreground = 3 } },
  1503. [5] = { { foreground = tonumber('0xff8000') }, {} },
  1504. })
  1505. child_exec_lua('vim.o.statusline="^^^^^^^"')
  1506. child_exec_lua('vim.o.termguicolors=true')
  1507. child_exec_lua('vim.cmd.terminal(...)', testprg('tty-test'))
  1508. screen:expect {
  1509. grid = [[
  1510. {1:t}ty ready |
  1511. |*3
  1512. {2:^^^^^^^ }|
  1513. |
  1514. {3:-- TERMINAL --} |
  1515. ]],
  1516. }
  1517. feed_data(
  1518. ':call chansend(&channel, "\\033[38;5;3mtext\\033[38:2:255:128:0mcolor\\033[0;10mtext")\n'
  1519. )
  1520. screen:expect {
  1521. grid = [[
  1522. {1:t}ty ready |
  1523. {4:text}{5:color}text |
  1524. |*2
  1525. {2:^^^^^^^ }|
  1526. |
  1527. {3:-- TERMINAL --} |
  1528. ]],
  1529. }
  1530. feed_data(':set notermguicolors\n')
  1531. screen:expect {
  1532. grid = [[
  1533. {1:t}ty ready |
  1534. {4:text}colortext |
  1535. |*2
  1536. {2:^^^^^^^ }|
  1537. :set notermguicolors |
  1538. {3:-- TERMINAL --} |
  1539. ]],
  1540. }
  1541. end)
  1542. -- Note: libvterm doesn't support colored underline or undercurl.
  1543. it('supports undercurl and underdouble when run in :terminal', function()
  1544. screen:set_default_attr_ids({
  1545. [1] = { reverse = true },
  1546. [2] = { bold = true, reverse = true },
  1547. [3] = { bold = true },
  1548. [4] = { foreground = 12 },
  1549. [5] = { undercurl = true },
  1550. [6] = { underdouble = true },
  1551. })
  1552. child_session:request('nvim_set_hl', 0, 'Visual', { undercurl = true })
  1553. feed_data('ifoobar\027V')
  1554. screen:expect([[
  1555. {5:fooba}{1:r} |
  1556. {4:~ }|*3
  1557. {2:[No Name] [+] }|
  1558. {3:-- VISUAL LINE --} |
  1559. {3:-- TERMINAL --} |
  1560. ]])
  1561. child_session:request('nvim_set_hl', 0, 'Visual', { underdouble = true })
  1562. screen:expect([[
  1563. {6:fooba}{1:r} |
  1564. {4:~ }|*3
  1565. {2:[No Name] [+] }|
  1566. {3:-- VISUAL LINE --} |
  1567. {3:-- TERMINAL --} |
  1568. ]])
  1569. end)
  1570. it('in nvim_list_uis()', function()
  1571. -- $TERM in :terminal.
  1572. local exp_term = is_os('bsd') and 'builtin_xterm' or 'xterm-256color'
  1573. local expected = {
  1574. {
  1575. chan = 1,
  1576. ext_cmdline = false,
  1577. ext_hlstate = false,
  1578. ext_linegrid = true,
  1579. ext_messages = false,
  1580. ext_multigrid = false,
  1581. ext_popupmenu = false,
  1582. ext_tabline = false,
  1583. ext_termcolors = true,
  1584. ext_wildmenu = false,
  1585. height = 6,
  1586. override = false,
  1587. rgb = false,
  1588. stdin_tty = true,
  1589. stdout_tty = true,
  1590. term_background = '',
  1591. term_colors = 256,
  1592. term_name = exp_term,
  1593. width = 50,
  1594. },
  1595. }
  1596. local _, rv = child_session:request('nvim_list_uis')
  1597. eq(expected, rv)
  1598. end)
  1599. it('allows grid to assume wider ambiwidth chars than host terminal', function()
  1600. child_session:request(
  1601. 'nvim_buf_set_lines',
  1602. 0,
  1603. 0,
  1604. -1,
  1605. true,
  1606. { ('℃'):rep(60), ('℃'):rep(60) }
  1607. )
  1608. child_session:request('nvim_set_option_value', 'cursorline', true, {})
  1609. child_session:request('nvim_set_option_value', 'list', true, {})
  1610. child_session:request('nvim_set_option_value', 'listchars', 'eol:$', { win = 0 })
  1611. feed_data('gg')
  1612. local singlewidth_screen = [[
  1613. {13:℃}{12:℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃}|
  1614. {12:℃℃℃℃℃℃℃℃℃℃}{15:$}{12: }|
  1615. ℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃|
  1616. ℃℃℃℃℃℃℃℃℃℃{4:$} |
  1617. {5:[No Name] [+] }|
  1618. |
  1619. {3:-- TERMINAL --} |
  1620. ]]
  1621. -- When grid assumes "℃" to be double-width but host terminal assumes it to be single-width,
  1622. -- the second cell of "℃" is a space and the attributes of the "℃" are applied to it.
  1623. local doublewidth_screen = [[
  1624. {13:℃}{12: ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
  1625. {12:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
  1626. {12:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }{15:$}{12: }|
  1627. ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ {4:@@@@}|
  1628. {5:[No Name] [+] }|
  1629. |
  1630. {3:-- TERMINAL --} |
  1631. ]]
  1632. screen:expect(singlewidth_screen)
  1633. child_session:request('nvim_set_option_value', 'ambiwidth', 'double', {})
  1634. screen:expect(doublewidth_screen)
  1635. child_session:request('nvim_set_option_value', 'ambiwidth', 'single', {})
  1636. screen:expect(singlewidth_screen)
  1637. child_session:request('nvim_call_function', 'setcellwidths', { { { 0x2103, 0x2103, 2 } } })
  1638. screen:expect(doublewidth_screen)
  1639. child_session:request('nvim_call_function', 'setcellwidths', { { { 0x2103, 0x2103, 1 } } })
  1640. screen:expect(singlewidth_screen)
  1641. end)
  1642. it('allows grid to assume wider non-ambiwidth chars than host terminal', function()
  1643. child_session:request(
  1644. 'nvim_buf_set_lines',
  1645. 0,
  1646. 0,
  1647. -1,
  1648. true,
  1649. { ('✓'):rep(60), ('✓'):rep(60) }
  1650. )
  1651. child_session:request('nvim_set_option_value', 'cursorline', true, {})
  1652. child_session:request('nvim_set_option_value', 'list', true, {})
  1653. child_session:request('nvim_set_option_value', 'listchars', 'eol:$', { win = 0 })
  1654. feed_data('gg')
  1655. local singlewidth_screen = [[
  1656. {13:✓}{12:✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓}|
  1657. {12:✓✓✓✓✓✓✓✓✓✓}{15:$}{12: }|
  1658. ✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓|
  1659. ✓✓✓✓✓✓✓✓✓✓{4:$} |
  1660. {5:[No Name] [+] }|
  1661. |
  1662. {3:-- TERMINAL --} |
  1663. ]]
  1664. -- When grid assumes "✓" to be double-width but host terminal assumes it to be single-width,
  1665. -- the second cell of "✓" is a space and the attributes of the "✓" are applied to it.
  1666. local doublewidth_screen = [[
  1667. {13:✓}{12: ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }|
  1668. {12:✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }|
  1669. {12:✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }{15:$}{12: }|
  1670. ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ {4:@@@@}|
  1671. {5:[No Name] [+] }|
  1672. |
  1673. {3:-- TERMINAL --} |
  1674. ]]
  1675. screen:expect(singlewidth_screen)
  1676. child_session:request('nvim_set_option_value', 'ambiwidth', 'double', {})
  1677. screen:expect_unchanged()
  1678. child_session:request('nvim_call_function', 'setcellwidths', { { { 0x2713, 0x2713, 2 } } })
  1679. screen:expect(doublewidth_screen)
  1680. child_session:request('nvim_set_option_value', 'ambiwidth', 'single', {})
  1681. screen:expect_unchanged()
  1682. child_session:request('nvim_call_function', 'setcellwidths', { { { 0x2713, 0x2713, 1 } } })
  1683. screen:expect(singlewidth_screen)
  1684. end)
  1685. it('draws correctly when cursor_address overflows #21643', function()
  1686. t.skip(is_os('mac'), 'FIXME: crashes/errors on macOS')
  1687. screen:try_resize(77, 855)
  1688. retry(nil, nil, function()
  1689. eq({ true, 852 }, { child_session:request('nvim_win_get_height', 0) })
  1690. end)
  1691. -- Use full screen message so that redrawing afterwards is more deterministic.
  1692. child_session:notify('nvim_command', 'intro')
  1693. screen:expect({ any = 'Nvim' })
  1694. -- Going to top-left corner needs 3 bytes.
  1695. -- Setting underline attribute needs 9 bytes.
  1696. -- The whole line needs 3 + 9 + 65513 + 3 = 65528 bytes.
  1697. -- The cursor_address that comes after will overflow the 65535-byte buffer.
  1698. local line = ('a'):rep(65513) .. '℃'
  1699. child_session:notify(
  1700. 'nvim_exec_lua',
  1701. [[
  1702. vim.api.nvim_buf_set_lines(0, 0, -1, true, {...})
  1703. vim.o.cursorline = true
  1704. ]],
  1705. { line, 'b' }
  1706. )
  1707. -- Close the :intro message and redraw the lines.
  1708. feed_data('\n')
  1709. screen:expect(
  1710. '{13:a}{12:'
  1711. .. ('a'):rep(76)
  1712. .. '}|\n'
  1713. .. ('{12:' .. ('a'):rep(77) .. '}|\n'):rep(849)
  1714. .. '{12:'
  1715. .. ('a'):rep(63)
  1716. .. '℃'
  1717. .. (' '):rep(13)
  1718. .. '}|\n'
  1719. .. dedent([[
  1720. b |
  1721. {5:[No Name] [+] }|
  1722. |
  1723. {3:-- TERMINAL --} |]])
  1724. )
  1725. end)
  1726. it('visual bell (padding) does not crash #21610', function()
  1727. feed_data ':set visualbell\n'
  1728. screen:expect {
  1729. grid = [[
  1730. {1: } |
  1731. {4:~ }|*3
  1732. {5:[No Name] }|
  1733. :set visualbell |
  1734. {3:-- TERMINAL --} |
  1735. ]],
  1736. }
  1737. -- move left is enough to invoke the bell
  1738. feed_data 'h'
  1739. -- visual change to show we process events after this
  1740. feed_data 'i'
  1741. screen:expect {
  1742. grid = [[
  1743. {1: } |
  1744. {4:~ }|*3
  1745. {5:[No Name] }|
  1746. {3:-- INSERT --} |
  1747. {3:-- TERMINAL --} |
  1748. ]],
  1749. }
  1750. end)
  1751. it('no assert failure on deadly signal #21896', function()
  1752. exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigterm')]])
  1753. screen:expect {
  1754. grid = [[
  1755. Vim: Caught deadly signal 'SIGTERM' |
  1756. |*2
  1757. [Process exited 1]{1: } |
  1758. |*2
  1759. {3:-- TERMINAL --} |
  1760. ]],
  1761. }
  1762. end)
  1763. it('no stack-use-after-scope with cursor color #22432', function()
  1764. screen:set_option('rgb', true)
  1765. command('set termguicolors')
  1766. child_session:request(
  1767. 'nvim_exec2',
  1768. [[
  1769. set tgc
  1770. hi Cursor guifg=Red guibg=Green
  1771. set guicursor=n:block-Cursor/lCursor
  1772. ]],
  1773. {}
  1774. )
  1775. screen:set_default_attr_ids({
  1776. [1] = { reverse = true },
  1777. [2] = { bold = true, foreground = Screen.colors.Blue },
  1778. [3] = { foreground = Screen.colors.Blue },
  1779. [4] = { reverse = true, bold = true },
  1780. [5] = { bold = true },
  1781. })
  1782. screen:expect([[
  1783. {1: } |
  1784. {2:~}{3: }|*3
  1785. {4:[No Name] }|
  1786. |
  1787. {5:-- TERMINAL --} |
  1788. ]])
  1789. feed_data('i')
  1790. screen:expect([[
  1791. {1: } |
  1792. {2:~}{3: }|*3
  1793. {4:[No Name] }|
  1794. {5:-- INSERT --} |
  1795. {5:-- TERMINAL --} |
  1796. ]])
  1797. end)
  1798. it('redraws on SIGWINCH even if terminal size is unchanged #23411', function()
  1799. child_session:request('nvim_echo', { { 'foo' } }, false, {})
  1800. screen:expect([[
  1801. {1: } |
  1802. {4:~ }|*3
  1803. {5:[No Name] }|
  1804. foo |
  1805. {3:-- TERMINAL --} |
  1806. ]])
  1807. exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigwinch')]])
  1808. screen:expect([[
  1809. {1: } |
  1810. {4:~ }|*3
  1811. {5:[No Name] }|
  1812. |
  1813. {3:-- TERMINAL --} |
  1814. ]])
  1815. end)
  1816. it('supports hiding cursor', function()
  1817. child_session:request(
  1818. 'nvim_command',
  1819. "let g:id = jobstart([v:progpath, '--clean', '--headless'])"
  1820. )
  1821. feed_data(':call jobwait([g:id])\n')
  1822. screen:expect([[
  1823. |
  1824. {4:~ }|*3
  1825. {5:[No Name] }|
  1826. :call jobwait([g:id]) |
  1827. {3:-- TERMINAL --} |
  1828. ]])
  1829. feed_data('\003')
  1830. screen:expect([[
  1831. {1: } |
  1832. {4:~ }|*3
  1833. {5:[No Name] }|
  1834. Type :qa and press <Enter> to exit Nvim |
  1835. {3:-- TERMINAL --} |
  1836. ]])
  1837. end)
  1838. it('cursor is not hidden on incsearch with no match', function()
  1839. feed_data('ifoo\027')
  1840. feed_data('/foo')
  1841. screen:expect([[
  1842. {1:foo} |
  1843. {4:~ }|*3
  1844. {5:[No Name] [+] }|
  1845. /foo{1: } |
  1846. {3:-- TERMINAL --} |
  1847. ]])
  1848. screen:sleep(10)
  1849. feed_data('b')
  1850. screen:expect([[
  1851. foo |
  1852. {4:~ }|*3
  1853. {5:[No Name] [+] }|
  1854. /foob{1: } |
  1855. {3:-- TERMINAL --} |
  1856. ]])
  1857. screen:sleep(10)
  1858. feed_data('a')
  1859. screen:expect([[
  1860. foo |
  1861. {4:~ }|*3
  1862. {5:[No Name] [+] }|
  1863. /fooba{1: } |
  1864. {3:-- TERMINAL --} |
  1865. ]])
  1866. end)
  1867. it('emits hyperlinks with OSC 8', function()
  1868. exec_lua([[
  1869. local buf = vim.api.nvim_get_current_buf()
  1870. _G.urls = {}
  1871. vim.api.nvim_create_autocmd('TermRequest', {
  1872. buffer = buf,
  1873. callback = function(args)
  1874. local req = args.data
  1875. if not req then
  1876. return
  1877. end
  1878. local id, url = req:match('\027]8;id=(%d+);(.*)$')
  1879. if id ~= nil and url ~= nil then
  1880. table.insert(_G.urls, { id = tonumber(id), url = url })
  1881. end
  1882. end,
  1883. })
  1884. ]])
  1885. child_exec_lua([[
  1886. vim.api.nvim_buf_set_lines(0, 0, 0, true, {'Hello'})
  1887. local ns = vim.api.nvim_create_namespace('test')
  1888. vim.api.nvim_buf_set_extmark(0, ns, 0, 1, {
  1889. end_col = 3,
  1890. url = 'https://example.com',
  1891. })
  1892. ]])
  1893. retry(nil, 1000, function()
  1894. eq({ { id = 0xE1EA0000, url = 'https://example.com' } }, exec_lua([[return _G.urls]]))
  1895. end)
  1896. end)
  1897. end)
  1898. describe('TUI', function()
  1899. before_each(clear)
  1900. it('resize at startup #17285 #15044 #11330', function()
  1901. local screen = Screen.new(50, 10)
  1902. screen:set_default_attr_ids({
  1903. [1] = { reverse = true },
  1904. [2] = { bold = true, foreground = Screen.colors.Blue },
  1905. [3] = { bold = true },
  1906. [4] = { foreground = tonumber('0x4040ff'), fg_indexed = true },
  1907. [5] = { bold = true, reverse = true },
  1908. })
  1909. screen:attach()
  1910. fn.termopen({
  1911. nvim_prog,
  1912. '--clean',
  1913. '--cmd',
  1914. 'colorscheme vim',
  1915. '--cmd',
  1916. 'set notermguicolors',
  1917. '--cmd',
  1918. 'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile',
  1919. }, {
  1920. env = {
  1921. VIMRUNTIME = os.getenv('VIMRUNTIME'),
  1922. },
  1923. })
  1924. exec([[
  1925. sleep 500m
  1926. vs new
  1927. ]])
  1928. screen:expect([[
  1929. ^ │ |
  1930. {2:~ }│{4:~ }|*5
  1931. {2:~ }│{5:[No Name] 0,0-1 All}|
  1932. {2:~ }│ |
  1933. {5:new }{1:{MATCH:<.*[/\]nvim }}|
  1934. |
  1935. ]])
  1936. end)
  1937. it('invalidated regions are cleared with terminal background attr', function()
  1938. local screen = Screen.new(50, 10)
  1939. screen:set_default_attr_ids({ [1] = { foreground = Screen.colors.Black } })
  1940. screen:attach()
  1941. fn.termopen({
  1942. nvim_prog,
  1943. '--clean',
  1944. '--cmd',
  1945. 'set termguicolors',
  1946. '--cmd',
  1947. 'sleep 10',
  1948. }, {
  1949. env = {
  1950. VIMRUNTIME = os.getenv('VIMRUNTIME'),
  1951. },
  1952. })
  1953. screen:expect({
  1954. grid = [[
  1955. {1:^ }|
  1956. {1: }|*8
  1957. |
  1958. ]],
  1959. })
  1960. screen:try_resize(51, 11)
  1961. screen:expect({
  1962. grid = [[
  1963. {1:^ }|
  1964. {1: }|*9
  1965. |
  1966. ]],
  1967. })
  1968. end)
  1969. it('argv[0] can be overridden #23953', function()
  1970. if not exec_lua('return pcall(require, "ffi")') then
  1971. pending('missing LuaJIT FFI')
  1972. end
  1973. local script_file = 'Xargv0.lua'
  1974. write_file(
  1975. script_file,
  1976. [=[
  1977. local ffi = require('ffi')
  1978. ffi.cdef([[int execl(const char *, const char *, ...);]])
  1979. ffi.C.execl(vim.v.progpath, 'Xargv0nvim', '--clean', nil)
  1980. ]=]
  1981. )
  1982. finally(function()
  1983. os.remove(script_file)
  1984. end)
  1985. local screen = tt.setup_child_nvim({ '--clean', '-l', script_file })
  1986. screen:expect {
  1987. grid = [[
  1988. {1: } |
  1989. ~ |*3
  1990. [No Name] 0,0-1 All|
  1991. |
  1992. {3:-- TERMINAL --} |
  1993. ]],
  1994. }
  1995. feed_data(':put =v:argv + [v:progname]\n')
  1996. screen:expect {
  1997. grid = [[
  1998. Xargv0nvim |
  1999. --embed |
  2000. --clean |
  2001. {1:X}argv0nvim |
  2002. [No Name] [+] 5,1 Bot|
  2003. 4 more lines |
  2004. {3:-- TERMINAL --} |
  2005. ]],
  2006. }
  2007. end)
  2008. it('with non-tty (pipe) stdout/stderr', function()
  2009. finally(function()
  2010. os.remove('testF')
  2011. end)
  2012. local screen = tt.screen_setup(
  2013. 0,
  2014. ('"%s" -u NONE -i NONE --cmd "set noswapfile noshowcmd noruler" --cmd "normal iabc" > /dev/null 2>&1 && cat testF && rm testF'):format(
  2015. nvim_prog
  2016. ),
  2017. nil,
  2018. { VIMRUNTIME = os.getenv('VIMRUNTIME') }
  2019. )
  2020. feed_data(':w testF\n:q\n')
  2021. screen:expect([[
  2022. :w testF |
  2023. :q |
  2024. abc |
  2025. |
  2026. [Process exited 0]{1: } |
  2027. |
  2028. {3:-- TERMINAL --} |
  2029. ]])
  2030. end)
  2031. it('<C-h> #10134', function()
  2032. local screen = tt.setup_child_nvim({
  2033. '-u',
  2034. 'NONE',
  2035. '-i',
  2036. 'NONE',
  2037. '--cmd',
  2038. 'colorscheme vim',
  2039. '--cmd',
  2040. 'set noruler notermguicolors',
  2041. '--cmd',
  2042. ':nnoremap <C-h> :echomsg "\\<C-h\\>"<CR>',
  2043. })
  2044. screen:expect {
  2045. grid = [[
  2046. {1: } |
  2047. {4:~ }|*3
  2048. {5:[No Name] }|
  2049. |
  2050. {3:-- TERMINAL --} |
  2051. ]],
  2052. }
  2053. command([[call chansend(b:terminal_job_id, "\<C-h>")]])
  2054. screen:expect([[
  2055. {1: } |
  2056. {4:~ }|*3
  2057. {5:[No Name] }|
  2058. <C-h> |
  2059. {3:-- TERMINAL --} |
  2060. ]])
  2061. end)
  2062. it('draws line with many trailing spaces correctly #24955', function()
  2063. local screen = tt.setup_child_nvim({
  2064. '-u',
  2065. 'NONE',
  2066. '-i',
  2067. 'NONE',
  2068. '--cmd',
  2069. 'set notermguicolors',
  2070. '--cmd',
  2071. 'colorscheme vim',
  2072. '--cmd',
  2073. 'call setline(1, ["1st line" .. repeat(" ", 153), "2nd line"])',
  2074. }, { cols = 80 })
  2075. screen:expect {
  2076. grid = [[
  2077. {1:1}st line |
  2078. |*2
  2079. 2nd line |
  2080. {5:[No Name] [+] 1,1 All}|
  2081. |
  2082. {3:-- TERMINAL --} |
  2083. ]],
  2084. }
  2085. feed_data('$')
  2086. screen:expect {
  2087. grid = [[
  2088. 1st line |
  2089. |
  2090. {1: } |
  2091. 2nd line |
  2092. {5:[No Name] [+] 1,161 All}|
  2093. |
  2094. {3:-- TERMINAL --} |
  2095. ]],
  2096. }
  2097. end)
  2098. it('draws screen lines with leading spaces correctly #29711', function()
  2099. local screen = tt.setup_child_nvim({
  2100. '-u',
  2101. 'NONE',
  2102. '-i',
  2103. 'NONE',
  2104. '--cmd',
  2105. 'set foldcolumn=6 | call setline(1, ["", repeat("aabb", 1000)]) | echo 42',
  2106. }, { extra_rows = 10, cols = 66 })
  2107. screen:expect {
  2108. grid = [[
  2109. |
  2110. aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabb|*12
  2111. aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabba@@@|
  2112. [No Name] [+] 1,0-1 Top|
  2113. 42 |
  2114. -- TERMINAL -- |
  2115. ]],
  2116. attr_ids = {},
  2117. }
  2118. feed_data('\12') -- Ctrl-L
  2119. -- The first line counts as 3 cells.
  2120. -- For the second line, 6 repeated spaces at the start counts as 2 cells,
  2121. -- so each screen line of the second line counts as 62 cells.
  2122. -- After drawing the first line and 8 screen lines of the second line,
  2123. -- 3 + 8 * 62 = 499 cells have been counted.
  2124. -- The 6 repeated spaces at the start of the next screen line exceeds the
  2125. -- 500-cell limit, so the buffer is flushed after these spaces.
  2126. screen:expect {
  2127. grid = [[
  2128. |
  2129. aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabb|*12
  2130. aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabba@@@|
  2131. [No Name] [+] 1,0-1 Top|
  2132. |
  2133. -- TERMINAL -- |
  2134. ]],
  2135. attr_ids = {},
  2136. }
  2137. end)
  2138. it('no heap-buffer-overflow when changing &columns', function()
  2139. -- Set a different bg colour and change $TERM to something dumber so the `print_spaces()`
  2140. -- codepath in `clear_region()` is hit.
  2141. local screen = tt.setup_child_nvim({
  2142. '-u',
  2143. 'NONE',
  2144. '-i',
  2145. 'NONE',
  2146. '--cmd',
  2147. 'set notermguicolors | highlight Normal ctermbg=red',
  2148. '--cmd',
  2149. 'call setline(1, ["a"->repeat(&columns)])',
  2150. }, { env = { TERM = 'ansi' } })
  2151. screen:expect {
  2152. grid = [[
  2153. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
  2154. ~ |*3
  2155. [No Name] [+] 1,1 All|
  2156. |
  2157. -- TERMINAL -- |
  2158. ]],
  2159. attr_ids = {},
  2160. }
  2161. feed_data(':set columns=12\n')
  2162. screen:expect {
  2163. grid = [[
  2164. aaaaaaaaaaaa |*4
  2165. < [+] 1,1 |
  2166. |
  2167. -- TERMINAL -- |
  2168. ]],
  2169. attr_ids = {},
  2170. }
  2171. -- Wider than TUI, so screen state will look weird.
  2172. -- Wait for the statusline to redraw to confirm that the TUI lives and ASAN is happy.
  2173. feed_data(':set columns=99|set stl=redrawn%m\n')
  2174. screen:expect({ any = 'redrawn%[%+%]' })
  2175. end)
  2176. end)
  2177. describe('TUI UIEnter/UILeave', function()
  2178. it('fires exactly once, after VimEnter', function()
  2179. clear()
  2180. local screen = tt.setup_child_nvim({
  2181. '-u',
  2182. 'NONE',
  2183. '-i',
  2184. 'NONE',
  2185. '--cmd',
  2186. 'colorscheme vim',
  2187. '--cmd',
  2188. 'set noswapfile noshowcmd noruler notermguicolors',
  2189. '--cmd',
  2190. 'let g:evs = []',
  2191. '--cmd',
  2192. 'autocmd UIEnter * :call add(g:evs, "UIEnter")',
  2193. '--cmd',
  2194. 'autocmd UILeave * :call add(g:evs, "UILeave")',
  2195. '--cmd',
  2196. 'autocmd VimEnter * :call add(g:evs, "VimEnter")',
  2197. })
  2198. screen:expect {
  2199. grid = [[
  2200. {1: } |
  2201. {4:~ }|*3
  2202. {5:[No Name] }|
  2203. |
  2204. {3:-- TERMINAL --} |
  2205. ]],
  2206. }
  2207. feed_data(':echo g:evs\n')
  2208. screen:expect {
  2209. grid = [[
  2210. {1: } |
  2211. {4:~ }|*3
  2212. {5:[No Name] }|
  2213. ['VimEnter', 'UIEnter'] |
  2214. {3:-- TERMINAL --} |
  2215. ]],
  2216. }
  2217. end)
  2218. end)
  2219. describe('TUI FocusGained/FocusLost', function()
  2220. local screen
  2221. local child_session
  2222. before_each(function()
  2223. clear()
  2224. local child_server = new_pipename()
  2225. screen = tt.setup_child_nvim({
  2226. '--listen',
  2227. child_server,
  2228. '-u',
  2229. 'NONE',
  2230. '-i',
  2231. 'NONE',
  2232. '--cmd',
  2233. 'colorscheme vim',
  2234. '--cmd',
  2235. 'set noswapfile noshowcmd noruler notermguicolors background=dark',
  2236. })
  2237. screen:expect([[
  2238. {1: } |
  2239. {4:~ }|*3
  2240. {5:[No Name] }|
  2241. |
  2242. {3:-- TERMINAL --} |
  2243. ]])
  2244. child_session = n.connect(child_server)
  2245. child_session:request(
  2246. 'nvim_exec2',
  2247. [[
  2248. autocmd FocusGained * echo 'gained'
  2249. autocmd FocusLost * echo 'lost'
  2250. ]],
  2251. {}
  2252. )
  2253. feed_data('\034\016') -- CTRL-\ CTRL-N
  2254. end)
  2255. it('in normal-mode', function()
  2256. retry(2, 3 * screen.timeout, function()
  2257. feed_data('\027[I')
  2258. screen:expect([[
  2259. {1: } |
  2260. {4:~ }|*3
  2261. {5:[No Name] }|
  2262. gained |
  2263. {3:-- TERMINAL --} |
  2264. ]])
  2265. feed_data('\027[O')
  2266. screen:expect([[
  2267. {1: } |
  2268. {4:~ }|*3
  2269. {5:[No Name] }|
  2270. lost |
  2271. {3:-- TERMINAL --} |
  2272. ]])
  2273. end)
  2274. end)
  2275. it('in insert-mode', function()
  2276. feed_data(':set noshowmode\r')
  2277. feed_data('i')
  2278. screen:expect {
  2279. grid = [[
  2280. {1: } |
  2281. {4:~ }|*3
  2282. {5:[No Name] }|
  2283. :set noshowmode |
  2284. {3:-- TERMINAL --} |
  2285. ]],
  2286. }
  2287. retry(2, 3 * screen.timeout, function()
  2288. feed_data('\027[I')
  2289. screen:expect([[
  2290. {1: } |
  2291. {4:~ }|*3
  2292. {5:[No Name] }|
  2293. gained |
  2294. {3:-- TERMINAL --} |
  2295. ]])
  2296. feed_data('\027[O')
  2297. screen:expect([[
  2298. {1: } |
  2299. {4:~ }|*3
  2300. {5:[No Name] }|
  2301. lost |
  2302. {3:-- TERMINAL --} |
  2303. ]])
  2304. end)
  2305. end)
  2306. -- During cmdline-mode we ignore :echo invoked by timers/events.
  2307. -- See commit: 5cc87d4dabd02167117be7a978b5c8faaa975419.
  2308. it('in cmdline-mode does NOT :echo', function()
  2309. feed_data(':')
  2310. feed_data('\027[I')
  2311. screen:expect([[
  2312. |
  2313. {4:~ }|*3
  2314. {5:[No Name] }|
  2315. :{1: } |
  2316. {3:-- TERMINAL --} |
  2317. ]])
  2318. feed_data('\027[O')
  2319. screen:expect {
  2320. grid = [[
  2321. |
  2322. {4:~ }|*3
  2323. {5:[No Name] }|
  2324. :{1: } |
  2325. {3:-- TERMINAL --} |
  2326. ]],
  2327. unchanged = true,
  2328. }
  2329. end)
  2330. it('in cmdline-mode', function()
  2331. -- Set up autocmds that modify the buffer, instead of just calling :echo.
  2332. -- This is how we can test handling of focus gained/lost during cmdline-mode.
  2333. -- See commit: 5cc87d4dabd02167117be7a978b5c8faaa975419.
  2334. child_session:request(
  2335. 'nvim_exec2',
  2336. [[
  2337. autocmd!
  2338. autocmd FocusLost * call append(line('$'), 'lost')
  2339. autocmd FocusGained * call append(line('$'), 'gained')
  2340. ]],
  2341. {}
  2342. )
  2343. retry(2, 3 * screen.timeout, function()
  2344. -- Enter cmdline-mode.
  2345. feed_data(':')
  2346. screen:sleep(1)
  2347. -- Send focus lost/gained termcodes.
  2348. feed_data('\027[O')
  2349. feed_data('\027[I')
  2350. screen:sleep(1)
  2351. -- Exit cmdline-mode. Redraws from timers/events are blocked during
  2352. -- cmdline-mode, so the buffer won't be updated until we exit cmdline-mode.
  2353. feed_data('\n')
  2354. screen:expect { any = 'lost' .. (' '):rep(46) .. '|\ngained' }
  2355. end)
  2356. end)
  2357. it('in terminal-mode', function()
  2358. feed_data(':set shell=' .. testprg('shell-test') .. ' shellcmdflag=EXE\n')
  2359. feed_data(':set noshowmode laststatus=0\n')
  2360. feed_data(':terminal zia\n')
  2361. -- Wait for terminal to be ready.
  2362. screen:expect {
  2363. grid = [[
  2364. {1:r}eady $ zia |
  2365. |
  2366. [Process exited 0] |
  2367. |*2
  2368. :terminal zia |
  2369. {3:-- TERMINAL --} |
  2370. ]],
  2371. }
  2372. feed_data('\027[I')
  2373. screen:expect {
  2374. grid = [[
  2375. {1:r}eady $ zia |
  2376. |
  2377. [Process exited 0] |
  2378. |*2
  2379. gained |
  2380. {3:-- TERMINAL --} |
  2381. ]],
  2382. timeout = (4 * screen.timeout),
  2383. }
  2384. feed_data('\027[O')
  2385. screen:expect([[
  2386. {1:r}eady $ zia |
  2387. |
  2388. [Process exited 0] |
  2389. |*2
  2390. lost |
  2391. {3:-- TERMINAL --} |
  2392. ]])
  2393. end)
  2394. it('in press-enter prompt', function()
  2395. feed_data(":echom 'msg1'|echom 'msg2'|echom 'msg3'|echom 'msg4'|echom 'msg5'\n")
  2396. -- Execute :messages to provoke the press-enter prompt.
  2397. feed_data(':messages\n')
  2398. screen:expect {
  2399. grid = [[
  2400. msg1 |
  2401. msg2 |
  2402. msg3 |
  2403. msg4 |
  2404. msg5 |
  2405. {10:Press ENTER or type command to continue}{1: } |
  2406. {3:-- TERMINAL --} |
  2407. ]],
  2408. }
  2409. feed_data('\027[I')
  2410. feed_data('\027[I')
  2411. screen:expect {
  2412. grid = [[
  2413. msg1 |
  2414. msg2 |
  2415. msg3 |
  2416. msg4 |
  2417. msg5 |
  2418. {10:Press ENTER or type command to continue}{1: } |
  2419. {3:-- TERMINAL --} |
  2420. ]],
  2421. unchanged = true,
  2422. }
  2423. end)
  2424. end)
  2425. -- These tests require `tt` because --headless/--embed
  2426. -- does not initialize the TUI.
  2427. describe("TUI 't_Co' (terminal colors)", function()
  2428. local screen
  2429. local function assert_term_colors(term, colorterm, maxcolors)
  2430. clear({ env = { TERM = term }, args = {} })
  2431. screen = tt.setup_child_nvim({
  2432. '-u',
  2433. 'NONE',
  2434. '-i',
  2435. 'NONE',
  2436. '--cmd',
  2437. 'colorscheme vim',
  2438. '--cmd',
  2439. nvim_set .. ' notermguicolors',
  2440. }, {
  2441. env = {
  2442. LANG = 'C',
  2443. TERM = term or '',
  2444. COLORTERM = colorterm or '',
  2445. },
  2446. })
  2447. local tline
  2448. if maxcolors == 8 then
  2449. tline = '{9:~ }'
  2450. elseif maxcolors == 16 then
  2451. tline = '~ '
  2452. else
  2453. tline = '{4:~ }'
  2454. end
  2455. screen:expect(string.format(
  2456. [[
  2457. {1: } |
  2458. %s|*4
  2459. |
  2460. {3:-- TERMINAL --} |
  2461. ]],
  2462. tline
  2463. ))
  2464. feed_data(':echo &t_Co\n')
  2465. screen:expect(string.format(
  2466. [[
  2467. {1: } |
  2468. %s|*4
  2469. %-3s |
  2470. {3:-- TERMINAL --} |
  2471. ]],
  2472. tline,
  2473. tostring(maxcolors and maxcolors or '')
  2474. ))
  2475. end
  2476. -- ansi and no terminal type at all:
  2477. it('no TERM uses 8 colors', function()
  2478. assert_term_colors(nil, nil, 8)
  2479. end)
  2480. it('TERM=ansi no COLORTERM uses 8 colors', function()
  2481. assert_term_colors('ansi', nil, 8)
  2482. end)
  2483. it('TERM=ansi with COLORTERM=anything-no-number uses 16 colors', function()
  2484. assert_term_colors('ansi', 'yet-another-term', 16)
  2485. end)
  2486. it('unknown TERM COLORTERM with 256 in name uses 256 colors', function()
  2487. assert_term_colors('ansi', 'yet-another-term-256color', 256)
  2488. end)
  2489. it('TERM=ansi-256color sets 256 colours', function()
  2490. assert_term_colors('ansi-256color', nil, 256)
  2491. end)
  2492. -- Unknown terminal types:
  2493. it('unknown TERM no COLORTERM sets 8 colours', function()
  2494. assert_term_colors('yet-another-term', nil, 8)
  2495. end)
  2496. it('unknown TERM with COLORTERM=anything-no-number uses 16 colors', function()
  2497. assert_term_colors('yet-another-term', 'yet-another-term', 16)
  2498. end)
  2499. it('unknown TERM with 256 in name sets 256 colours', function()
  2500. assert_term_colors('yet-another-term-256color', nil, 256)
  2501. end)
  2502. it('unknown TERM COLORTERM with 256 in name uses 256 colors', function()
  2503. assert_term_colors('yet-another-term', 'yet-another-term-256color', 256)
  2504. end)
  2505. -- Linux kernel terminal emulator:
  2506. it('TERM=linux uses 256 colors', function()
  2507. assert_term_colors('linux', nil, 256)
  2508. end)
  2509. it('TERM=linux-16color uses 256 colors', function()
  2510. assert_term_colors('linux-16color', nil, 256)
  2511. end)
  2512. it('TERM=linux-256color uses 256 colors', function()
  2513. assert_term_colors('linux-256color', nil, 256)
  2514. end)
  2515. -- screen:
  2516. --
  2517. -- FreeBSD falls back to the built-in screen-256colour entry.
  2518. -- Linux and MacOS have a screen entry in external terminfo with 8 colours,
  2519. -- which is raised to 16 by COLORTERM.
  2520. it('TERM=screen no COLORTERM uses 8/256 colors', function()
  2521. if is_os('freebsd') then
  2522. assert_term_colors('screen', nil, 256)
  2523. else
  2524. assert_term_colors('screen', nil, 8)
  2525. end
  2526. end)
  2527. it('TERM=screen COLORTERM=screen uses 16/256 colors', function()
  2528. if is_os('freebsd') then
  2529. assert_term_colors('screen', 'screen', 256)
  2530. else
  2531. assert_term_colors('screen', 'screen', 16)
  2532. end
  2533. end)
  2534. it('TERM=screen COLORTERM=screen-256color uses 256 colors', function()
  2535. assert_term_colors('screen', 'screen-256color', 256)
  2536. end)
  2537. it('TERM=screen-256color no COLORTERM uses 256 colors', function()
  2538. assert_term_colors('screen-256color', nil, 256)
  2539. end)
  2540. -- tmux:
  2541. --
  2542. -- FreeBSD and MacOS fall back to the built-in tmux-256colour entry.
  2543. -- Linux has a tmux entry in external terminfo with 8 colours,
  2544. -- which is raised to 256.
  2545. it('TERM=tmux no COLORTERM uses 256 colors', function()
  2546. assert_term_colors('tmux', nil, 256)
  2547. end)
  2548. it('TERM=tmux COLORTERM=tmux uses 256 colors', function()
  2549. assert_term_colors('tmux', 'tmux', 256)
  2550. end)
  2551. it('TERM=tmux COLORTERM=tmux-256color uses 256 colors', function()
  2552. assert_term_colors('tmux', 'tmux-256color', 256)
  2553. end)
  2554. it('TERM=tmux-256color no COLORTERM uses 256 colors', function()
  2555. assert_term_colors('tmux-256color', nil, 256)
  2556. end)
  2557. -- xterm and imitators:
  2558. it('TERM=xterm uses 256 colors', function()
  2559. assert_term_colors('xterm', nil, 256)
  2560. end)
  2561. it('TERM=xterm COLORTERM=gnome-terminal uses 256 colors', function()
  2562. assert_term_colors('xterm', 'gnome-terminal', 256)
  2563. end)
  2564. it('TERM=xterm COLORTERM=mate-terminal uses 256 colors', function()
  2565. assert_term_colors('xterm', 'mate-terminal', 256)
  2566. end)
  2567. it('TERM=xterm-256color uses 256 colors', function()
  2568. assert_term_colors('xterm-256color', nil, 256)
  2569. end)
  2570. -- rxvt and stterm:
  2571. --
  2572. -- FreeBSD and MacOS fall back to the built-in rxvt-256color and
  2573. -- st-256colour entries.
  2574. -- Linux has an rxvt, an st, and an st-16color entry in external terminfo
  2575. -- with 8, 8, and 16 colours respectively, which are raised to 256.
  2576. it('TERM=rxvt no COLORTERM uses 256 colors', function()
  2577. assert_term_colors('rxvt', nil, 256)
  2578. end)
  2579. it('TERM=rxvt COLORTERM=rxvt uses 256 colors', function()
  2580. assert_term_colors('rxvt', 'rxvt', 256)
  2581. end)
  2582. it('TERM=rxvt-256color uses 256 colors', function()
  2583. assert_term_colors('rxvt-256color', nil, 256)
  2584. end)
  2585. it('TERM=st no COLORTERM uses 256 colors', function()
  2586. assert_term_colors('st', nil, 256)
  2587. end)
  2588. it('TERM=st COLORTERM=st uses 256 colors', function()
  2589. assert_term_colors('st', 'st', 256)
  2590. end)
  2591. it('TERM=st COLORTERM=st-256color uses 256 colors', function()
  2592. assert_term_colors('st', 'st-256color', 256)
  2593. end)
  2594. it('TERM=st-16color no COLORTERM uses 8/256 colors', function()
  2595. assert_term_colors('st', nil, 256)
  2596. end)
  2597. it('TERM=st-16color COLORTERM=st uses 16/256 colors', function()
  2598. assert_term_colors('st', 'st', 256)
  2599. end)
  2600. it('TERM=st-16color COLORTERM=st-256color uses 256 colors', function()
  2601. assert_term_colors('st', 'st-256color', 256)
  2602. end)
  2603. it('TERM=st-256color uses 256 colors', function()
  2604. assert_term_colors('st-256color', nil, 256)
  2605. end)
  2606. -- gnome and vte:
  2607. --
  2608. -- FreeBSD and MacOS fall back to the built-in vte-256color entry.
  2609. -- Linux has a gnome, a vte, a gnome-256color, and a vte-256color entry in
  2610. -- external terminfo with 8, 8, 256, and 256 colours respectively, which are
  2611. -- raised to 256.
  2612. it('TERM=gnome no COLORTERM uses 256 colors', function()
  2613. assert_term_colors('gnome', nil, 256)
  2614. end)
  2615. it('TERM=gnome COLORTERM=gnome uses 256 colors', function()
  2616. assert_term_colors('gnome', 'gnome', 256)
  2617. end)
  2618. it('TERM=gnome COLORTERM=gnome-256color uses 256 colors', function()
  2619. assert_term_colors('gnome', 'gnome-256color', 256)
  2620. end)
  2621. it('TERM=gnome-256color uses 256 colors', function()
  2622. assert_term_colors('gnome-256color', nil, 256)
  2623. end)
  2624. it('TERM=vte no COLORTERM uses 256 colors', function()
  2625. assert_term_colors('vte', nil, 256)
  2626. end)
  2627. it('TERM=vte COLORTERM=vte uses 256 colors', function()
  2628. assert_term_colors('vte', 'vte', 256)
  2629. end)
  2630. it('TERM=vte COLORTERM=vte-256color uses 256 colors', function()
  2631. assert_term_colors('vte', 'vte-256color', 256)
  2632. end)
  2633. it('TERM=vte-256color uses 256 colors', function()
  2634. assert_term_colors('vte-256color', nil, 256)
  2635. end)
  2636. -- others:
  2637. -- TODO(blueyed): this is made pending, since it causes failure + later hang
  2638. -- when using non-compatible libvterm (#9494/#10179).
  2639. pending('TERM=interix uses 8 colors', function()
  2640. assert_term_colors('interix', nil, 8)
  2641. end)
  2642. it('TERM=iTerm.app uses 256 colors', function()
  2643. assert_term_colors('iTerm.app', nil, 256)
  2644. end)
  2645. it('TERM=iterm uses 256 colors', function()
  2646. assert_term_colors('iterm', nil, 256)
  2647. end)
  2648. end)
  2649. -- These tests require `tt` because --headless/--embed
  2650. -- does not initialize the TUI.
  2651. describe("TUI 'term' option", function()
  2652. local screen
  2653. local function assert_term(term_envvar, term_expected)
  2654. clear()
  2655. screen = tt.setup_child_nvim({
  2656. '-u',
  2657. 'NONE',
  2658. '-i',
  2659. 'NONE',
  2660. '--cmd',
  2661. nvim_set .. ' notermguicolors',
  2662. }, {
  2663. env = {
  2664. LANG = 'C',
  2665. TERM = term_envvar or '',
  2666. },
  2667. })
  2668. local full_timeout = screen.timeout
  2669. retry(nil, 2 * full_timeout, function() -- Wait for TUI thread to set 'term'.
  2670. feed_data(":echo 'term='.(&term)\n")
  2671. screen:expect { any = 'term=' .. term_expected, timeout = 250 }
  2672. end)
  2673. end
  2674. it('gets builtin term if $TERM is invalid', function()
  2675. assert_term('foo', 'builtin_ansi')
  2676. end)
  2677. it('gets system-provided term if $TERM is valid', function()
  2678. if is_os('openbsd') then
  2679. assert_term('xterm', 'xterm')
  2680. elseif is_os('bsd') then -- BSD lacks terminfo, builtin is always used.
  2681. assert_term('xterm', 'builtin_xterm')
  2682. elseif is_os('mac') then
  2683. local status, _ = pcall(assert_term, 'xterm', 'xterm')
  2684. if not status then
  2685. pending('macOS: unibilium could not find terminfo')
  2686. end
  2687. else
  2688. assert_term('xterm', 'xterm')
  2689. end
  2690. end)
  2691. it('builtin terms', function()
  2692. -- These non-standard terminfos are always builtin.
  2693. assert_term('win32con', 'builtin_win32con')
  2694. assert_term('conemu', 'builtin_conemu')
  2695. assert_term('vtpcon', 'builtin_vtpcon')
  2696. end)
  2697. end)
  2698. -- These tests require `tt` because --headless/--embed
  2699. -- does not initialize the TUI.
  2700. describe('TUI', function()
  2701. local screen
  2702. local logfile = 'Xtest_tui_verbose_log'
  2703. after_each(function()
  2704. os.remove(logfile)
  2705. end)
  2706. -- Runs (child) `nvim` in a TTY (:terminal), to start the builtin TUI.
  2707. local function nvim_tui(extra_args)
  2708. clear()
  2709. screen = tt.setup_child_nvim({
  2710. '-u',
  2711. 'NONE',
  2712. '-i',
  2713. 'NONE',
  2714. '--cmd',
  2715. 'colorscheme vim',
  2716. '--cmd',
  2717. nvim_set .. ' notermguicolors',
  2718. extra_args,
  2719. }, {
  2720. env = {
  2721. LANG = 'C',
  2722. },
  2723. })
  2724. end
  2725. it('-V3log logs terminfo values', function()
  2726. nvim_tui('-V3' .. logfile)
  2727. -- Wait for TUI to start.
  2728. feed_data('Gitext')
  2729. screen:expect([[
  2730. text{1: } |
  2731. {4:~ }|*4
  2732. {3:-- INSERT --} |
  2733. {3:-- TERMINAL --} |
  2734. ]])
  2735. retry(nil, 3000, function() -- Wait for log file to be flushed.
  2736. local log = read_file('Xtest_tui_verbose_log') or ''
  2737. eq('--- Terminal info --- {{{\n', string.match(log, '%-%-%- Terminal.-\n')) -- }}}
  2738. ok(#log > 50)
  2739. end)
  2740. end)
  2741. it('does not crash on large inputs #26099', function()
  2742. nvim_tui()
  2743. screen:expect([[
  2744. {1: } |
  2745. {4:~ }|*4
  2746. |
  2747. {3:-- TERMINAL --} |
  2748. ]])
  2749. feed_data(string.format('\027]52;c;%s\027\\', string.rep('A', 8192)))
  2750. screen:expect {
  2751. grid = [[
  2752. {1: } |
  2753. {4:~ }|*4
  2754. |
  2755. {3:-- TERMINAL --} |
  2756. ]],
  2757. unchanged = true,
  2758. }
  2759. end)
  2760. it('queries the terminal for truecolor support', function()
  2761. clear()
  2762. exec_lua([[
  2763. vim.api.nvim_create_autocmd('TermRequest', {
  2764. callback = function(args)
  2765. local req = args.data
  2766. local payload = req:match('^\027P%+q([%x;]+)$')
  2767. if payload then
  2768. local t = {}
  2769. for cap in vim.gsplit(payload, ';') do
  2770. local resp = string.format('\027P1+r%s\027\\', payload)
  2771. vim.api.nvim_chan_send(vim.bo[args.buf].channel, resp)
  2772. t[vim.text.hexdecode(cap)] = true
  2773. end
  2774. vim.g.xtgettcap = t
  2775. return true
  2776. end
  2777. end,
  2778. })
  2779. ]])
  2780. local child_server = new_pipename()
  2781. screen = tt.setup_child_nvim({
  2782. '--listen',
  2783. child_server,
  2784. '-u',
  2785. 'NONE',
  2786. '-i',
  2787. 'NONE',
  2788. }, {
  2789. env = {
  2790. VIMRUNTIME = os.getenv('VIMRUNTIME'),
  2791. -- Force COLORTERM to be unset and use a TERM that does not contain Tc or RGB in terminfo.
  2792. -- This will force the nested nvim instance to query with XTGETTCAP
  2793. COLORTERM = '',
  2794. TERM = 'xterm-256colors',
  2795. },
  2796. })
  2797. screen:expect({ any = '%[No Name%]' })
  2798. local child_session = n.connect(child_server)
  2799. retry(nil, 1000, function()
  2800. eq({
  2801. Tc = true,
  2802. RGB = true,
  2803. setrgbf = true,
  2804. setrgbb = true,
  2805. }, eval("get(g:, 'xtgettcap', '')"))
  2806. eq({ true, 1 }, { child_session:request('nvim_eval', '&termguicolors') })
  2807. end)
  2808. end)
  2809. it('does not query the terminal for truecolor support if $COLORTERM is set', function()
  2810. clear()
  2811. exec_lua([[
  2812. vim.api.nvim_create_autocmd('TermRequest', {
  2813. callback = function(args)
  2814. local req = args.data
  2815. vim.g.termrequest = req
  2816. local xtgettcap = req:match('^\027P%+q([%x;]+)$')
  2817. if xtgettcap then
  2818. local t = {}
  2819. for cap in vim.gsplit(xtgettcap, ';') do
  2820. local resp = string.format('\027P1+r%s\027\\', xtgettcap)
  2821. vim.api.nvim_chan_send(vim.bo[args.buf].channel, resp)
  2822. t[vim.text.hexdecode(cap)] = true
  2823. end
  2824. vim.g.xtgettcap = t
  2825. return true
  2826. elseif req:match('^\027P$qm\027\\$') then
  2827. vim.g.decrqss = true
  2828. end
  2829. end,
  2830. })
  2831. ]])
  2832. local child_server = new_pipename()
  2833. screen = tt.setup_child_nvim({
  2834. '--listen',
  2835. child_server,
  2836. '-u',
  2837. 'NONE',
  2838. '-i',
  2839. 'NONE',
  2840. }, {
  2841. env = {
  2842. VIMRUNTIME = os.getenv('VIMRUNTIME'),
  2843. -- With COLORTERM=256, Nvim should not query the terminal and should not set 'tgc'
  2844. COLORTERM = '256',
  2845. TERM = 'xterm-256colors',
  2846. },
  2847. })
  2848. screen:expect({ any = '%[No Name%]' })
  2849. local child_session = n.connect(child_server)
  2850. retry(nil, 1000, function()
  2851. local xtgettcap = eval("get(g:, 'xtgettcap', {})")
  2852. eq(nil, xtgettcap['Tc'])
  2853. eq(nil, xtgettcap['RGB'])
  2854. eq(nil, xtgettcap['setrgbf'])
  2855. eq(nil, xtgettcap['setrgbb'])
  2856. eq(0, eval([[get(g:, 'decrqss')]]))
  2857. eq({ true, 0 }, { child_session:request('nvim_eval', '&termguicolors') })
  2858. end)
  2859. end)
  2860. it('queries the terminal for OSC 52 support', function()
  2861. clear()
  2862. exec_lua([[
  2863. vim.api.nvim_create_autocmd('TermRequest', {
  2864. callback = function(args)
  2865. local req = args.data
  2866. local payload = req:match('^\027P%+q([%x;]+)$')
  2867. if payload and vim.text.hexdecode(payload) == 'Ms' then
  2868. vim.g.xtgettcap = 'Ms'
  2869. local resp = string.format('\027P1+r%s=%s\027\\', payload, vim.text.hexencode('\027]52;;\027\\'))
  2870. vim.api.nvim_chan_send(vim.bo[args.buf].channel, resp)
  2871. return true
  2872. end
  2873. end,
  2874. })
  2875. ]])
  2876. local child_server = new_pipename()
  2877. screen = tt.setup_child_nvim({
  2878. '--listen',
  2879. child_server,
  2880. -- Use --clean instead of -u NONE to load the osc52 plugin
  2881. '--clean',
  2882. }, {
  2883. env = {
  2884. VIMRUNTIME = os.getenv('VIMRUNTIME'),
  2885. -- Only queries when SSH_TTY is set
  2886. SSH_TTY = '/dev/pts/1',
  2887. },
  2888. })
  2889. screen:expect({ any = '%[No Name%]' })
  2890. local child_session = n.connect(child_server)
  2891. retry(nil, 1000, function()
  2892. eq('Ms', eval("get(g:, 'xtgettcap', '')"))
  2893. eq({ true, 'OSC 52' }, { child_session:request('nvim_eval', 'g:clipboard.name') })
  2894. end)
  2895. end)
  2896. end)
  2897. describe('TUI bg color', function()
  2898. before_each(clear)
  2899. it('is properly set in a nested Nvim instance when background=dark', function()
  2900. command('highlight clear Normal')
  2901. command('set background=dark') -- set outer Nvim background
  2902. local child_server = new_pipename()
  2903. local screen = tt.setup_child_nvim({
  2904. '--listen',
  2905. child_server,
  2906. '-u',
  2907. 'NONE',
  2908. '-i',
  2909. 'NONE',
  2910. '--cmd',
  2911. 'colorscheme vim',
  2912. '--cmd',
  2913. 'set noswapfile',
  2914. })
  2915. screen:expect({ any = '%[No Name%]' })
  2916. local child_session = n.connect(child_server)
  2917. retry(nil, nil, function()
  2918. eq({ true, 'dark' }, { child_session:request('nvim_eval', '&background') })
  2919. end)
  2920. end)
  2921. it('is properly set in a nested Nvim instance when background=light', function()
  2922. command('highlight clear Normal')
  2923. command('set background=light') -- set outer Nvim background
  2924. local child_server = new_pipename()
  2925. local screen = tt.setup_child_nvim({
  2926. '--listen',
  2927. child_server,
  2928. '-u',
  2929. 'NONE',
  2930. '-i',
  2931. 'NONE',
  2932. '--cmd',
  2933. 'colorscheme vim',
  2934. '--cmd',
  2935. 'set noswapfile',
  2936. })
  2937. screen:expect({ any = '%[No Name%]' })
  2938. local child_session = n.connect(child_server)
  2939. retry(nil, nil, function()
  2940. eq({ true, 'light' }, { child_session:request('nvim_eval', '&background') })
  2941. end)
  2942. end)
  2943. it('queries the terminal for background color', function()
  2944. exec_lua([[
  2945. vim.api.nvim_create_autocmd('TermRequest', {
  2946. callback = function(args)
  2947. local req = args.data
  2948. if req == '\027]11;?' then
  2949. vim.g.oscrequest = true
  2950. return true
  2951. end
  2952. end,
  2953. })
  2954. ]])
  2955. tt.setup_child_nvim({
  2956. '-u',
  2957. 'NONE',
  2958. '-i',
  2959. 'NONE',
  2960. '--cmd',
  2961. 'colorscheme vim',
  2962. '--cmd',
  2963. 'set noswapfile',
  2964. })
  2965. retry(nil, 1000, function()
  2966. eq(true, eval("get(g:, 'oscrequest', v:false)"))
  2967. end)
  2968. end)
  2969. it('triggers OptionSet from automatic background processing', function()
  2970. local screen = tt.setup_child_nvim({
  2971. '-u',
  2972. 'NONE',
  2973. '-i',
  2974. 'NONE',
  2975. '--cmd',
  2976. 'colorscheme vim',
  2977. '--cmd',
  2978. 'set noswapfile',
  2979. '-c',
  2980. 'autocmd OptionSet background echo "did OptionSet, yay!"',
  2981. })
  2982. screen:expect([[
  2983. {1: } |
  2984. {3:~} |*3
  2985. {5:[No Name] 0,0-1 All}|
  2986. did OptionSet, yay! |
  2987. {3:-- TERMINAL --} |
  2988. ]])
  2989. end)
  2990. end)
  2991. -- These tests require `tt` because --headless/--embed
  2992. -- does not initialize the TUI.
  2993. describe('TUI as a client', function()
  2994. after_each(function()
  2995. os.remove(testlog)
  2996. end)
  2997. it('connects to remote instance (with its own TUI)', function()
  2998. local server_super = spawn_argv(false) -- equivalent to clear()
  2999. local client_super = spawn_argv(true)
  3000. set_session(server_super)
  3001. local server_pipe = new_pipename()
  3002. local screen_server = tt.setup_child_nvim({
  3003. '--listen',
  3004. server_pipe,
  3005. '-u',
  3006. 'NONE',
  3007. '-i',
  3008. 'NONE',
  3009. '--cmd',
  3010. 'colorscheme vim',
  3011. '--cmd',
  3012. nvim_set .. ' notermguicolors laststatus=2 background=dark',
  3013. })
  3014. feed_data('iHello, World')
  3015. screen_server:expect {
  3016. grid = [[
  3017. Hello, World{1: } |
  3018. {4:~ }|*3
  3019. {5:[No Name] [+] }|
  3020. {3:-- INSERT --} |
  3021. {3:-- TERMINAL --} |
  3022. ]],
  3023. }
  3024. feed_data('\027')
  3025. screen_server:expect {
  3026. grid = [[
  3027. Hello, Worl{1:d} |
  3028. {4:~ }|*3
  3029. {5:[No Name] [+] }|
  3030. |
  3031. {3:-- TERMINAL --} |
  3032. ]],
  3033. }
  3034. set_session(client_super)
  3035. local screen_client = tt.setup_child_nvim({
  3036. '--server',
  3037. server_pipe,
  3038. '--remote-ui',
  3039. })
  3040. screen_client:expect {
  3041. grid = [[
  3042. Hello, Worl{1:d} |
  3043. {4:~ }|*3
  3044. {5:[No Name] [+] }|
  3045. |
  3046. {3:-- TERMINAL --} |
  3047. ]],
  3048. }
  3049. -- grid smaller than containing terminal window is cleared properly
  3050. feed_data(":call setline(1,['a'->repeat(&columns)]->repeat(&lines))\n")
  3051. feed_data('0:set lines=3\n')
  3052. screen_server:expect {
  3053. grid = [[
  3054. {1:a}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
  3055. {5:[No Name] [+] }|
  3056. |*4
  3057. {3:-- TERMINAL --} |
  3058. ]],
  3059. }
  3060. feed_data(':q!\n')
  3061. server_super:close()
  3062. client_super:close()
  3063. end)
  3064. it('connects to remote instance (--headless)', function()
  3065. local server = spawn_argv(false) -- equivalent to clear()
  3066. local client_super = spawn_argv(true, { env = { NVIM_LOG_FILE = testlog } })
  3067. set_session(server)
  3068. local server_pipe = api.nvim_get_vvar('servername')
  3069. server:request('nvim_input', 'iHalloj!<Esc>')
  3070. server:request('nvim_command', 'set notermguicolors')
  3071. set_session(client_super)
  3072. local screen_client = tt.setup_child_nvim({
  3073. '--server',
  3074. server_pipe,
  3075. '--remote-ui',
  3076. })
  3077. screen_client:expect {
  3078. grid = [[
  3079. Halloj{1:!} |
  3080. {4:~ }|*4
  3081. |
  3082. {3:-- TERMINAL --} |
  3083. ]],
  3084. }
  3085. -- No heap-use-after-free when receiving UI events after deadly signal #22184
  3086. server:request('nvim_input', ('a'):rep(1000))
  3087. exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigterm')]])
  3088. screen_client:expect {
  3089. grid = [[
  3090. Vim: Caught deadly signal 'SIGTERM' |
  3091. |*2
  3092. [Process exited 1]{1: } |
  3093. |*2
  3094. {3:-- TERMINAL --} |
  3095. ]],
  3096. }
  3097. eq(0, api.nvim_get_vvar('shell_error'))
  3098. -- exits on input eof #22244
  3099. fn.system({ nvim_prog, '--server', server_pipe, '--remote-ui' })
  3100. eq(1, api.nvim_get_vvar('shell_error'))
  3101. client_super:close()
  3102. server:close()
  3103. if is_os('mac') then
  3104. assert_log('uv_tty_set_mode failed: Unknown system error %-102', testlog)
  3105. end
  3106. end)
  3107. it('throws error when no server exists', function()
  3108. clear()
  3109. local screen = tt.setup_child_nvim({
  3110. '--server',
  3111. '127.0.0.1:2436546',
  3112. '--remote-ui',
  3113. }, { cols = 60 })
  3114. screen:expect([[
  3115. Remote ui failed to start: {MATCH:.*}|
  3116. |
  3117. [Process exited 1]{1: } |
  3118. |*3
  3119. {3:-- TERMINAL --} |
  3120. ]])
  3121. end)
  3122. local function test_remote_tui_quit(status)
  3123. local server_super = spawn_argv(false) -- equivalent to clear()
  3124. local client_super = spawn_argv(true)
  3125. set_session(server_super)
  3126. local server_pipe = new_pipename()
  3127. local screen_server = tt.setup_child_nvim({
  3128. '--listen',
  3129. server_pipe,
  3130. '-u',
  3131. 'NONE',
  3132. '-i',
  3133. 'NONE',
  3134. '--cmd',
  3135. 'colorscheme vim',
  3136. '--cmd',
  3137. nvim_set .. ' notermguicolors laststatus=2 background=dark',
  3138. })
  3139. screen_server:expect {
  3140. grid = [[
  3141. {1: } |
  3142. {4:~ }|*3
  3143. {5:[No Name] }|
  3144. |
  3145. {3:-- TERMINAL --} |
  3146. ]],
  3147. }
  3148. feed_data('iHello, World')
  3149. screen_server:expect {
  3150. grid = [[
  3151. Hello, World{1: } |
  3152. {4:~ }|*3
  3153. {5:[No Name] [+] }|
  3154. {3:-- INSERT --} |
  3155. {3:-- TERMINAL --} |
  3156. ]],
  3157. }
  3158. feed_data('\027')
  3159. screen_server:expect {
  3160. grid = [[
  3161. Hello, Worl{1:d} |
  3162. {4:~ }|*3
  3163. {5:[No Name] [+] }|
  3164. |
  3165. {3:-- TERMINAL --} |
  3166. ]],
  3167. }
  3168. set_session(client_super)
  3169. local screen_client = tt.setup_child_nvim({
  3170. '--server',
  3171. server_pipe,
  3172. '--remote-ui',
  3173. })
  3174. screen_client:expect {
  3175. grid = [[
  3176. Hello, Worl{1:d} |
  3177. {4:~ }|*3
  3178. {5:[No Name] [+] }|
  3179. |
  3180. {3:-- TERMINAL --} |
  3181. ]],
  3182. }
  3183. -- quitting the server
  3184. set_session(server_super)
  3185. feed_data(status and ':' .. status .. 'cquit!\n' or ':quit!\n')
  3186. status = status and status or 0
  3187. screen_server:expect {
  3188. grid = [[
  3189. |
  3190. [Process exited ]] .. status .. [[]{1: }{MATCH:%s+}|
  3191. |*4
  3192. {3:-- TERMINAL --} |
  3193. ]],
  3194. }
  3195. -- assert that client has exited
  3196. screen_client:expect {
  3197. grid = [[
  3198. |
  3199. [Process exited ]] .. status .. [[]{1: }{MATCH:%s+}|
  3200. |*4
  3201. {3:-- TERMINAL --} |
  3202. ]],
  3203. }
  3204. server_super:close()
  3205. client_super:close()
  3206. end
  3207. describe('exits when server quits', function()
  3208. it('with :quit', function()
  3209. test_remote_tui_quit()
  3210. end)
  3211. it('with :cquit', function()
  3212. test_remote_tui_quit(42)
  3213. end)
  3214. end)
  3215. end)