12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165 |
- local t = require('test.testutil')
- local n = require('test.functional.testnvim')()
- local Screen = require('test.functional.ui.screen')
- local request = n.request
- local eq = t.eq
- local ok = t.ok
- local pcall_err = t.pcall_err
- local insert = n.insert
- local feed = n.feed
- local clear = n.clear
- local command = n.command
- local exec = n.exec
- local api = n.api
- local assert_alive = n.assert_alive
- local function expect(contents)
- return eq(contents, n.curbuf_contents())
- end
- local function set_extmark(ns_id, id, line, col, opts)
- if opts == nil then
- opts = {}
- end
- if id ~= nil and id ~= 0 then
- opts.id = id
- end
- return api.nvim_buf_set_extmark(0, ns_id, line, col, opts)
- end
- local function get_extmarks(ns_id, start, end_, opts)
- if opts == nil then
- opts = {}
- end
- return api.nvim_buf_get_extmarks(0, ns_id, start, end_, opts)
- end
- local function get_extmark_by_id(ns_id, id, opts)
- if opts == nil then
- opts = {}
- end
- return api.nvim_buf_get_extmark_by_id(0, ns_id, id, opts)
- end
- local function check_undo_redo(ns, mark, sr, sc, er, ec) --s = start, e = end
- local rv = get_extmark_by_id(ns, mark)
- eq({ er, ec }, rv)
- feed('u')
- rv = get_extmark_by_id(ns, mark)
- eq({ sr, sc }, rv)
- feed('<c-r>')
- rv = get_extmark_by_id(ns, mark)
- eq({ er, ec }, rv)
- end
- local function batch_set(ns_id, positions)
- local ids = {}
- for _, pos in ipairs(positions) do
- table.insert(ids, set_extmark(ns_id, 0, pos[1], pos[2]))
- end
- return ids
- end
- local function batch_check(ns_id, ids, positions)
- local actual, expected = {}, {}
- for i, id in ipairs(ids) do
- expected[id] = positions[i]
- end
- for _, mark in pairs(get_extmarks(ns_id, 0, -1, {})) do
- actual[mark[1]] = { mark[2], mark[3] }
- end
- eq(expected, actual)
- end
- local function batch_check_undo_redo(ns_id, ids, before, after)
- batch_check(ns_id, ids, after)
- feed('u')
- batch_check(ns_id, ids, before)
- feed('<c-r>')
- batch_check(ns_id, ids, after)
- end
- describe('API/extmarks', function()
- local screen
- local marks, positions, init_text, row, col
- local ns, ns2
- before_each(function()
- -- Initialize some namespaces and insert 12345 into a buffer
- marks = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
- positions = { { 0, 0 }, { 0, 2 }, { 0, 3 } }
- init_text = '12345'
- row = 0
- col = 2
- clear()
- insert(init_text)
- ns = request('nvim_create_namespace', 'my-fancy-plugin')
- ns2 = request('nvim_create_namespace', 'my-fancy-plugin2')
- end)
- it('validation', function()
- eq(
- "Invalid 'end_col': expected Integer, got Array",
- pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = {}, end_row = 1 })
- )
- eq(
- "Invalid 'end_row': expected Integer, got Array",
- pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = {} })
- )
- eq(
- "Invalid 'virt_text_pos': expected String, got Integer",
- pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 0 })
- )
- eq(
- "Invalid 'virt_text_pos': 'foo'",
- pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 'foo' })
- )
- eq(
- "Invalid 'hl_mode': expected String, got Integer",
- pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 0 })
- )
- eq("Invalid 'hl_mode': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 'foo' }))
- eq(
- "Invalid 'id': expected Integer, got Array",
- pcall_err(set_extmark, ns, {}, 0, 0, { end_col = 1, end_row = 1 })
- )
- eq(
- 'Invalid mark position: expected 2 Integer items',
- pcall_err(get_extmarks, ns, {}, { -1, -1 })
- )
- eq(
- 'Invalid mark position: expected mark id Integer or 2-item Array',
- pcall_err(get_extmarks, ns, true, { -1, -1 })
- )
- -- No memory leak with virt_text, virt_lines, sign_text
- eq(
- 'right_gravity is not a boolean',
- pcall_err(set_extmark, ns, marks[2], 0, 0, {
- virt_text = { { 'foo', 'Normal' } },
- virt_lines = { { { 'bar', 'Normal' } } },
- sign_text = 'a',
- right_gravity = 'baz',
- })
- )
- end)
- it('can end extranges past final newline using end_col = 0', function()
- set_extmark(ns, marks[1], 0, 0, {
- end_col = 0,
- end_row = 1,
- })
- eq(
- "Invalid 'end_col': out of range",
- pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 })
- )
- end)
- it('can end extranges past final newline when strict mode is false', function()
- set_extmark(ns, marks[1], 0, 0, {
- end_col = 1,
- end_row = 1,
- strict = false,
- })
- end)
- it('can end extranges past final column when strict mode is false', function()
- set_extmark(ns, marks[1], 0, 0, {
- end_col = 6,
- end_row = 0,
- strict = false,
- })
- end)
- it('adds, updates and deletes marks', function()
- local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
- eq(marks[1], rv)
- rv = get_extmark_by_id(ns, marks[1])
- eq({ positions[1][1], positions[1][2] }, rv)
- -- Test adding a second mark on same row works
- rv = set_extmark(ns, marks[2], positions[2][1], positions[2][2])
- eq(marks[2], rv)
- -- Test an update, (same pos)
- rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
- eq(marks[1], rv)
- rv = get_extmark_by_id(ns, marks[2])
- eq({ positions[2][1], positions[2][2] }, rv)
- -- Test an update, (new pos)
- row = positions[1][1]
- col = positions[1][2] + 1
- rv = set_extmark(ns, marks[1], row, col)
- eq(marks[1], rv)
- rv = get_extmark_by_id(ns, marks[1])
- eq({ row, col }, rv)
- -- remove the test marks
- eq(true, api.nvim_buf_del_extmark(0, ns, marks[1]))
- eq(false, api.nvim_buf_del_extmark(0, ns, marks[1]))
- eq(true, api.nvim_buf_del_extmark(0, ns, marks[2]))
- eq(false, api.nvim_buf_del_extmark(0, ns, marks[3]))
- eq(false, api.nvim_buf_del_extmark(0, ns, 1000))
- end)
- it('can clear a specific namespace range', function()
- set_extmark(ns, 1, 0, 1)
- set_extmark(ns2, 1, 0, 1)
- -- force a new undo buffer
- feed('o<esc>')
- api.nvim_buf_clear_namespace(0, ns2, 0, -1)
- eq({ { 1, 0, 1 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 }))
- feed('u')
- eq({ { 1, 0, 1 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 }))
- feed('<c-r>')
- eq({ { 1, 0, 1 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 }))
- end)
- it('can clear a namespace range using 0,-1', function()
- set_extmark(ns, 1, 0, 1)
- set_extmark(ns2, 1, 0, 1)
- -- force a new undo buffer
- feed('o<esc>')
- api.nvim_buf_clear_namespace(0, -1, 0, -1)
- eq({}, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 }))
- feed('u')
- eq({}, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 }))
- feed('<c-r>')
- eq({}, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 }))
- end)
- it('can undo with extmarks (#25147)', function()
- feed('itest<esc>')
- set_extmark(ns, 1, 0, 0)
- set_extmark(ns, 2, 1, 0)
- eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- feed('dd')
- eq({ { 1, 1, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- api.nvim_buf_clear_namespace(0, ns, 0, -1)
- eq({}, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- set_extmark(ns, 1, 0, 0, { right_gravity = false })
- set_extmark(ns, 2, 1, 0, { right_gravity = false })
- eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- feed('u')
- eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
- api.nvim_buf_clear_namespace(0, ns, 0, -1)
- end)
- it('querying for information and ranges', function()
- --marks = {1, 2, 3}
- --positions = {{0, 0,}, {0, 2}, {0, 3}}
- -- add some more marks
- for i, m in ipairs(marks) do
- if positions[i] ~= nil then
- local rv = set_extmark(ns, m, positions[i][1], positions[i][2])
- eq(m, rv)
- end
- end
- -- {0, 0} and {-1, -1} work as extreme values
- eq({ { 1, 0, 0 } }, get_extmarks(ns, { 0, 0 }, { 0, 0 }))
- eq({}, get_extmarks(ns, { -1, -1 }, { -1, -1 }))
- local rv = get_extmarks(ns, { 0, 0 }, { -1, -1 })
- for i, m in ipairs(marks) do
- if positions[i] ~= nil then
- eq({ m, positions[i][1], positions[i][2] }, rv[i])
- end
- end
- -- 0 and -1 works as short hand extreme values
- eq({ { 1, 0, 0 } }, get_extmarks(ns, 0, 0))
- eq({}, get_extmarks(ns, -1, -1))
- rv = get_extmarks(ns, 0, -1)
- for i, m in ipairs(marks) do
- if positions[i] ~= nil then
- eq({ m, positions[i][1], positions[i][2] }, rv[i])
- end
- end
- -- next with mark id
- rv = get_extmarks(ns, marks[1], { -1, -1 }, { limit = 1 })
- eq({ { marks[1], positions[1][1], positions[1][2] } }, rv)
- rv = get_extmarks(ns, marks[2], { -1, -1 }, { limit = 1 })
- eq({ { marks[2], positions[2][1], positions[2][2] } }, rv)
- -- next with positional when mark exists at position
- rv = get_extmarks(ns, positions[1], { -1, -1 }, { limit = 1 })
- eq({ { marks[1], positions[1][1], positions[1][2] } }, rv)
- -- next with positional index (no mark at position)
- rv = get_extmarks(ns, { positions[1][1], positions[1][2] + 1 }, { -1, -1 }, { limit = 1 })
- eq({ { marks[2], positions[2][1], positions[2][2] } }, rv)
- -- next with Extremity index
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 1 })
- eq({ { marks[1], positions[1][1], positions[1][2] } }, rv)
- -- nextrange with mark id
- rv = get_extmarks(ns, marks[1], marks[3])
- eq({ marks[1], positions[1][1], positions[1][2] }, rv[1])
- eq({ marks[2], positions[2][1], positions[2][2] }, rv[2])
- -- nextrange with `limit`
- rv = get_extmarks(ns, marks[1], marks[3], { limit = 2 })
- eq(2, #rv)
- -- nextrange with positional when mark exists at position
- rv = get_extmarks(ns, positions[1], positions[3])
- eq({ marks[1], positions[1][1], positions[1][2] }, rv[1])
- eq({ marks[2], positions[2][1], positions[2][2] }, rv[2])
- rv = get_extmarks(ns, positions[2], positions[3])
- eq(2, #rv)
- -- nextrange with positional index (no mark at position)
- local lower = { positions[1][1], positions[2][2] - 1 }
- local upper = { positions[2][1], positions[3][2] - 1 }
- rv = get_extmarks(ns, lower, upper)
- eq({ { marks[2], positions[2][1], positions[2][2] } }, rv)
- lower = { positions[3][1], positions[3][2] + 1 }
- upper = { positions[3][1], positions[3][2] + 2 }
- rv = get_extmarks(ns, lower, upper)
- eq({}, rv)
- -- nextrange with extremity index
- lower = { positions[2][1], positions[2][2] + 1 }
- upper = { -1, -1 }
- rv = get_extmarks(ns, lower, upper)
- eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
- -- prev with mark id
- rv = get_extmarks(ns, marks[3], { 0, 0 }, { limit = 1 })
- eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
- rv = get_extmarks(ns, marks[2], { 0, 0 }, { limit = 1 })
- eq({ { marks[2], positions[2][1], positions[2][2] } }, rv)
- -- prev with positional when mark exists at position
- rv = get_extmarks(ns, positions[3], { 0, 0 }, { limit = 1 })
- eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
- -- prev with positional index (no mark at position)
- rv = get_extmarks(ns, { positions[1][1], positions[1][2] + 1 }, { 0, 0 }, { limit = 1 })
- eq({ { marks[1], positions[1][1], positions[1][2] } }, rv)
- -- prev with Extremity index
- rv = get_extmarks(ns, { -1, -1 }, { 0, 0 }, { limit = 1 })
- eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
- -- prevrange with mark id
- rv = get_extmarks(ns, marks[3], marks[1])
- eq({ marks[3], positions[3][1], positions[3][2] }, rv[1])
- eq({ marks[2], positions[2][1], positions[2][2] }, rv[2])
- eq({ marks[1], positions[1][1], positions[1][2] }, rv[3])
- -- prevrange with limit
- rv = get_extmarks(ns, marks[3], marks[1], { limit = 2 })
- eq(2, #rv)
- -- prevrange with positional when mark exists at position
- rv = get_extmarks(ns, positions[3], positions[1])
- eq({
- { marks[3], positions[3][1], positions[3][2] },
- { marks[2], positions[2][1], positions[2][2] },
- { marks[1], positions[1][1], positions[1][2] },
- }, rv)
- rv = get_extmarks(ns, positions[2], positions[1])
- eq(2, #rv)
- -- prevrange with positional index (no mark at position)
- lower = { positions[2][1], positions[2][2] + 1 }
- upper = { positions[3][1], positions[3][2] + 1 }
- rv = get_extmarks(ns, upper, lower)
- eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
- lower = { positions[3][1], positions[3][2] + 1 }
- upper = { positions[3][1], positions[3][2] + 2 }
- rv = get_extmarks(ns, upper, lower)
- eq({}, rv)
- -- prevrange with extremity index
- lower = { 0, 0 }
- upper = { positions[2][1], positions[2][2] - 1 }
- rv = get_extmarks(ns, upper, lower)
- eq({ { marks[1], positions[1][1], positions[1][2] } }, rv)
- end)
- it('querying for information with limit', function()
- -- add some more marks
- for i, m in ipairs(marks) do
- if positions[i] ~= nil then
- local rv = set_extmark(ns, m, positions[i][1], positions[i][2])
- eq(m, rv)
- end
- end
- local rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 1 })
- eq(1, #rv)
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 2 })
- eq(2, #rv)
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 3 })
- eq(3, #rv)
- -- now in reverse
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 1 })
- eq(1, #rv)
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 2 })
- eq(2, #rv)
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 3 })
- eq(3, #rv)
- end)
- it('get_marks works when mark col > upper col', function()
- feed('A<cr>12345<esc>')
- feed('A<cr>12345<esc>')
- set_extmark(ns, 10, 0, 2) -- this shouldn't be found
- set_extmark(ns, 11, 2, 1) -- this shouldn't be found
- set_extmark(ns, marks[1], 0, 4) -- check col > our upper bound
- set_extmark(ns, marks[2], 1, 1) -- check col < lower bound
- set_extmark(ns, marks[3], 2, 0) -- check is inclusive
- eq(
- { { marks[1], 0, 4 }, { marks[2], 1, 1 }, { marks[3], 2, 0 } },
- get_extmarks(ns, { 0, 3 }, { 2, 0 })
- )
- end)
- it('get_marks works in reverse when mark col < lower col', function()
- feed('A<cr>12345<esc>')
- feed('A<cr>12345<esc>')
- set_extmark(ns, 10, 0, 1) -- this shouldn't be found
- set_extmark(ns, 11, 2, 4) -- this shouldn't be found
- set_extmark(ns, marks[1], 2, 1) -- check col < our lower bound
- set_extmark(ns, marks[2], 1, 4) -- check col > upper bound
- set_extmark(ns, marks[3], 0, 2) -- check is inclusive
- local rv = get_extmarks(ns, { 2, 3 }, { 0, 2 })
- eq({ { marks[1], 2, 1 }, { marks[2], 1, 4 }, { marks[3], 0, 2 } }, rv)
- end)
- it('get_marks limit=0 returns nothing', function()
- set_extmark(ns, marks[1], positions[1][1], positions[1][2])
- local rv = get_extmarks(ns, { -1, -1 }, { -1, -1 }, { limit = 0 })
- eq({}, rv)
- end)
- it('marks move with line insertations', function()
- set_extmark(ns, marks[1], 0, 0)
- feed('yyP')
- check_undo_redo(ns, marks[1], 0, 0, 1, 0)
- end)
- it('marks move with multiline insertations', function()
- feed('a<cr>22<cr>33<esc>')
- set_extmark(ns, marks[1], 1, 1)
- feed('ggVGyP')
- check_undo_redo(ns, marks[1], 1, 1, 4, 1)
- end)
- it('marks move with line join', function()
- -- do_join in ops.c
- feed('a<cr>222<esc>')
- set_extmark(ns, marks[1], 1, 0)
- feed('ggJ')
- check_undo_redo(ns, marks[1], 1, 0, 0, 6)
- end)
- it('join works when no marks are present', function()
- screen = Screen.new(15, 10)
- feed('a<cr>1<esc>')
- feed('kJ')
- -- This shouldn't seg fault
- screen:expect([[
- 12345^ 1 |
- {1:~ }|*8
- |
- ]])
- end)
- it('marks move with multiline join', function()
- -- do_join in ops.c
- feed('a<cr>222<cr>333<cr>444<esc>')
- set_extmark(ns, marks[1], 3, 0)
- feed('2GVGJ')
- check_undo_redo(ns, marks[1], 3, 0, 1, 8)
- end)
- it('marks move with line deletes', function()
- feed('a<cr>222<cr>333<cr>444<esc>')
- set_extmark(ns, marks[1], 2, 1)
- feed('ggjdd')
- check_undo_redo(ns, marks[1], 2, 1, 1, 1)
- end)
- it('marks move with multiline deletes', function()
- feed('a<cr>222<cr>333<cr>444<esc>')
- set_extmark(ns, marks[1], 3, 0)
- feed('gg2dd')
- check_undo_redo(ns, marks[1], 3, 0, 1, 0)
- -- regression test, undoing multiline delete when mark is on row 1
- feed('ugg3dd')
- check_undo_redo(ns, marks[1], 3, 0, 0, 0)
- end)
- it('marks move with open line', function()
- -- open_line in change.c
- -- testing marks below are also moved
- feed('yyP')
- set_extmark(ns, marks[1], 0, 4)
- set_extmark(ns, marks[2], 1, 4)
- feed('1G<s-o><esc>')
- check_undo_redo(ns, marks[1], 0, 4, 1, 4)
- check_undo_redo(ns, marks[2], 1, 4, 2, 4)
- feed('2Go<esc>')
- check_undo_redo(ns, marks[1], 1, 4, 1, 4)
- check_undo_redo(ns, marks[2], 2, 4, 3, 4)
- end)
- it('marks move with char inserts', function()
- -- insertchar in edit.c (the ins_str branch)
- screen = Screen.new(15, 10)
- set_extmark(ns, marks[1], 0, 3)
- feed('0')
- insert('abc')
- screen:expect([[
- ab^c12345 |
- {1:~ }|*8
- |
- ]])
- local rv = get_extmark_by_id(ns, marks[1])
- eq({ 0, 6 }, rv)
- check_undo_redo(ns, marks[1], 0, 3, 0, 6)
- end)
- -- gravity right as definted in tk library
- it('marks have gravity right', function()
- -- insertchar in edit.c (the ins_str branch)
- set_extmark(ns, marks[1], 0, 2)
- feed('03l')
- insert('X')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- -- check multibyte chars
- feed('03l<esc>')
- insert('~~')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- end)
- it('we can insert multibyte chars', function()
- -- insertchar in edit.c
- feed('a<cr>12345<esc>')
- set_extmark(ns, marks[1], 1, 2)
- -- Insert a fullwidth (two col) tilde, NICE
- feed('0i~<esc>')
- check_undo_redo(ns, marks[1], 1, 2, 1, 5)
- end)
- it('marks move with blockwise inserts', function()
- -- op_insert in ops.c
- feed('a<cr>12345<esc>')
- set_extmark(ns, marks[1], 1, 2)
- feed('0<c-v>lkI9<esc>')
- check_undo_redo(ns, marks[1], 1, 2, 1, 3)
- end)
- it('marks move with line splits (using enter)', function()
- -- open_line in change.c
- -- testing marks below are also moved
- feed('yyP')
- set_extmark(ns, marks[1], 0, 4)
- set_extmark(ns, marks[2], 1, 4)
- feed('1Gla<cr><esc>')
- check_undo_redo(ns, marks[1], 0, 4, 1, 2)
- check_undo_redo(ns, marks[2], 1, 4, 2, 4)
- end)
- it('marks at last line move on insert new line', function()
- -- open_line in change.c
- set_extmark(ns, marks[1], 0, 4)
- feed('0i<cr><esc>')
- check_undo_redo(ns, marks[1], 0, 4, 1, 4)
- end)
- it('yet again marks move with line splits', function()
- -- the first test above wasn't catching all errors..
- feed('A67890<esc>')
- set_extmark(ns, marks[1], 0, 4)
- feed('04li<cr><esc>')
- check_undo_redo(ns, marks[1], 0, 4, 1, 0)
- end)
- it('and one last time line splits...', function()
- set_extmark(ns, marks[1], 0, 1)
- set_extmark(ns, marks[2], 0, 2)
- feed('02li<cr><esc>')
- check_undo_redo(ns, marks[1], 0, 1, 0, 1)
- check_undo_redo(ns, marks[2], 0, 2, 1, 0)
- end)
- it('multiple marks move with mark splits', function()
- set_extmark(ns, marks[1], 0, 1)
- set_extmark(ns, marks[2], 0, 3)
- feed('0li<cr><esc>')
- check_undo_redo(ns, marks[1], 0, 1, 1, 0)
- check_undo_redo(ns, marks[2], 0, 3, 1, 2)
- end)
- it('deleting right before a mark works', function()
- -- op_delete in ops.c
- set_extmark(ns, marks[1], 0, 2)
- feed('0lx')
- check_undo_redo(ns, marks[1], 0, 2, 0, 1)
- end)
- it('deleting right after a mark works', function()
- -- op_delete in ops.c
- set_extmark(ns, marks[1], 0, 2)
- feed('02lx')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- end)
- it('marks move with char deletes', function()
- -- op_delete in ops.c
- set_extmark(ns, marks[1], 0, 2)
- feed('02dl')
- check_undo_redo(ns, marks[1], 0, 2, 0, 0)
- -- from the other side (nothing should happen)
- feed('$x')
- check_undo_redo(ns, marks[1], 0, 0, 0, 0)
- end)
- it('marks move with char deletes over a range', function()
- -- op_delete in ops.c
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- feed('0l3dl<esc>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 1)
- check_undo_redo(ns, marks[2], 0, 3, 0, 1)
- -- delete 1, nothing should happen to our marks
- feed('u')
- feed('$x')
- check_undo_redo(ns, marks[2], 0, 3, 0, 3)
- end)
- it('deleting marks at end of line works', function()
- set_extmark(ns, marks[1], 0, 4)
- feed('$x')
- check_undo_redo(ns, marks[1], 0, 4, 0, 4)
- -- check the copy happened correctly on delete at eol
- feed('$x')
- check_undo_redo(ns, marks[1], 0, 4, 0, 3)
- feed('u')
- check_undo_redo(ns, marks[1], 0, 4, 0, 4)
- end)
- it('marks move with blockwise deletes', function()
- -- op_delete in ops.c
- feed('a<cr>12345<esc>')
- set_extmark(ns, marks[1], 1, 4)
- feed('h<c-v>hhkd')
- check_undo_redo(ns, marks[1], 1, 4, 1, 1)
- end)
- it('marks move with blockwise deletes over a range', function()
- -- op_delete in ops.c
- feed('a<cr>12345<esc>')
- set_extmark(ns, marks[1], 0, 1)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 1, 2)
- feed('0<c-v>k3lx')
- check_undo_redo(ns, marks[1], 0, 1, 0, 0)
- check_undo_redo(ns, marks[2], 0, 3, 0, 0)
- check_undo_redo(ns, marks[3], 1, 2, 1, 0)
- -- delete 1, nothing should happen to our marks
- feed('u')
- feed('$<c-v>jx')
- check_undo_redo(ns, marks[2], 0, 3, 0, 3)
- check_undo_redo(ns, marks[3], 1, 2, 1, 2)
- end)
- it('works with char deletes over multilines', function()
- feed('a<cr>12345<cr>test-me<esc>')
- set_extmark(ns, marks[1], 2, 5)
- feed('gg')
- feed('dv?-m?<cr>')
- check_undo_redo(ns, marks[1], 2, 5, 0, 0)
- end)
- it('marks outside of deleted range move with visual char deletes', function()
- -- op_delete in ops.c
- set_extmark(ns, marks[1], 0, 3)
- feed('0vx<esc>')
- check_undo_redo(ns, marks[1], 0, 3, 0, 2)
- feed('u')
- feed('0vlx<esc>')
- check_undo_redo(ns, marks[1], 0, 3, 0, 1)
- feed('u')
- feed('0v2lx<esc>')
- check_undo_redo(ns, marks[1], 0, 3, 0, 0)
- -- from the other side (nothing should happen)
- feed('$vx')
- check_undo_redo(ns, marks[1], 0, 0, 0, 0)
- end)
- it('marks outside of deleted range move with char deletes', function()
- -- op_delete in ops.c
- set_extmark(ns, marks[1], 0, 3)
- feed('0x<esc>')
- check_undo_redo(ns, marks[1], 0, 3, 0, 2)
- feed('u')
- feed('02x<esc>')
- check_undo_redo(ns, marks[1], 0, 3, 0, 1)
- feed('u')
- feed('0v3lx<esc>')
- check_undo_redo(ns, marks[1], 0, 3, 0, 0)
- -- from the other side (nothing should happen)
- feed('u')
- feed('$vx')
- check_undo_redo(ns, marks[1], 0, 3, 0, 3)
- end)
- it('marks move with P(backward) paste', function()
- -- do_put in ops.c
- feed('0iabc<esc>')
- set_extmark(ns, marks[1], 0, 7)
- feed('0veyP')
- check_undo_redo(ns, marks[1], 0, 7, 0, 15)
- end)
- it('marks move with p(forward) paste', function()
- -- do_put in ops.c
- feed('0iabc<esc>')
- set_extmark(ns, marks[1], 0, 7)
- feed('0veyp')
- check_undo_redo(ns, marks[1], 0, 7, 0, 15)
- end)
- it('marks move with blockwise P(backward) paste', function()
- -- do_put in ops.c
- feed('a<cr>12345<esc>')
- set_extmark(ns, marks[1], 1, 4)
- feed('<c-v>hhkyP<esc>')
- check_undo_redo(ns, marks[1], 1, 4, 1, 7)
- end)
- it('marks move with blockwise p(forward) paste', function()
- -- do_put in ops.c
- feed('a<cr>12345<esc>')
- set_extmark(ns, marks[1], 1, 4)
- feed('<c-v>hhkyp<esc>')
- check_undo_redo(ns, marks[1], 1, 4, 1, 7)
- end)
- describe('multiline regions', function()
- before_each(function()
- feed('dd')
- -- Achtung: code has been spiced with some unicode,
- -- to make life more interesting.
- -- luacheck whines about TABs inside strings for whatever reason.
- -- luacheck: push ignore 621
- insert([[
- static int nlua_rpcrequest(lua_State *lstate)
- {
- Ïf (!nlua_is_deferred_safe(lstate)) {
- // strictly not allowed
- Яetörn luaL_error(lstate, e_fast_api_disabled, "rpcrequest");
- }
- return nlua_rpc(lstate, true);
- }]])
- -- luacheck: pop
- end)
- it('delete', function()
- local pos1 = {
- { 2, 4 },
- { 2, 12 },
- { 2, 13 },
- { 2, 14 },
- { 2, 25 },
- { 4, 8 },
- { 4, 10 },
- { 4, 20 },
- { 5, 3 },
- { 6, 10 },
- }
- local ids = batch_set(ns, pos1)
- batch_check(ns, ids, pos1)
- feed('3Gfiv2+ftd')
- batch_check_undo_redo(ns, ids, pos1, {
- { 2, 4 },
- { 2, 12 },
- { 2, 13 },
- { 2, 13 },
- { 2, 13 },
- { 2, 13 },
- { 2, 15 },
- { 2, 25 },
- { 3, 3 },
- { 4, 10 },
- })
- end)
- it('can get overlapping extmarks', function()
- set_extmark(ns, 1, 0, 0, { end_row = 5, end_col = 0 })
- set_extmark(ns, 2, 2, 5, { end_row = 2, end_col = 30 })
- set_extmark(ns, 3, 0, 5, { end_row = 2, end_col = 10 })
- set_extmark(ns, 4, 0, 0, { end_row = 1, end_col = 0 })
- eq({ { 2, 2, 5 } }, get_extmarks(ns, { 2, 0 }, { 2, -1 }, { overlap = false }))
- eq(
- { { 1, 0, 0 }, { 3, 0, 5 }, { 2, 2, 5 } },
- get_extmarks(ns, { 2, 0 }, { 2, -1 }, { overlap = true })
- )
- end)
- end)
- it('replace works', function()
- set_extmark(ns, marks[1], 0, 2)
- feed('0r2')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- end)
- it('blockwise replace works', function()
- feed('a<cr>12345<esc>')
- set_extmark(ns, marks[1], 0, 2)
- feed('0<c-v>llkr1<esc>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 3)
- end)
- it('shift line', function()
- -- shift_line in ops.c
- feed(':set shiftwidth=4<cr><esc>')
- set_extmark(ns, marks[1], 0, 2)
- feed('0>>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 6)
- expect(' 12345')
- feed('>>')
- -- this is counter-intuitive. But what happens
- -- is that 4 spaces gets extended to one tab (== 8 spaces)
- check_undo_redo(ns, marks[1], 0, 6, 0, 3)
- expect('\t12345')
- feed('<LT><LT>') -- have to escape, same as <<
- check_undo_redo(ns, marks[1], 0, 3, 0, 6)
- end)
- it('blockwise shift', function()
- -- shift_block in ops.c
- feed(':set shiftwidth=4<cr><esc>')
- feed('a<cr>12345<esc>')
- set_extmark(ns, marks[1], 1, 2)
- feed('0<c-v>k>')
- check_undo_redo(ns, marks[1], 1, 2, 1, 6)
- feed('<c-v>j>')
- expect('\t12345\n\t12345')
- check_undo_redo(ns, marks[1], 1, 6, 1, 3)
- feed('<c-v>j<LT>')
- check_undo_redo(ns, marks[1], 1, 3, 1, 6)
- end)
- it('tab works with expandtab', function()
- -- ins_tab in edit.c
- feed(':set expandtab<cr><esc>')
- feed(':set shiftwidth=2<cr><esc>')
- set_extmark(ns, marks[1], 0, 2)
- feed('0i<tab><tab><esc>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 6)
- end)
- it('tabs work', function()
- -- ins_tab in edit.c
- feed(':set noexpandtab<cr><esc>')
- feed(':set shiftwidth=2<cr><esc>')
- feed(':set softtabstop=2<cr><esc>')
- feed(':set tabstop=8<cr><esc>')
- set_extmark(ns, marks[1], 0, 2)
- feed('0i<tab><esc>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 4)
- feed('0iX<tab><esc>')
- check_undo_redo(ns, marks[1], 0, 4, 0, 6)
- end)
- it('marks move when using :move', function()
- set_extmark(ns, marks[1], 0, 0)
- feed('A<cr>2<esc>:1move 2<cr><esc>')
- check_undo_redo(ns, marks[1], 0, 0, 1, 0)
- -- test codepath when moving lines up
- feed(':2move 0<cr><esc>')
- check_undo_redo(ns, marks[1], 1, 0, 0, 0)
- end)
- it('marks move when using :move part 2', function()
- -- make sure we didn't get lucky with the math...
- feed('A<cr>2<cr>3<cr>4<cr>5<cr>6<esc>')
- set_extmark(ns, marks[1], 1, 0)
- feed(':2,3move 5<cr><esc>')
- check_undo_redo(ns, marks[1], 1, 0, 3, 0)
- -- test codepath when moving lines up
- feed(':4,5move 1<cr><esc>')
- check_undo_redo(ns, marks[1], 3, 0, 1, 0)
- end)
- it('undo and redo of set and unset marks', function()
- -- Force a new undo head
- feed('o<esc>')
- set_extmark(ns, marks[1], 0, 1)
- feed('o<esc>')
- set_extmark(ns, marks[2], 0, -1)
- set_extmark(ns, marks[3], 0, -1)
- feed('u')
- local rv = get_extmarks(ns, { 0, 0 }, { -1, -1 })
- eq(3, #rv)
- feed('<c-r>')
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 })
- eq(3, #rv)
- -- Test updates
- feed('o<esc>')
- set_extmark(ns, marks[1], positions[1][1], positions[1][2])
- rv = get_extmarks(ns, marks[1], marks[1], { limit = 1 })
- eq(1, #rv)
- feed('u')
- feed('<c-r>')
- -- old value is NOT kept in history
- check_undo_redo(
- ns,
- marks[1],
- positions[1][1],
- positions[1][2],
- positions[1][1],
- positions[1][2]
- )
- -- Test unset
- feed('o<esc>')
- api.nvim_buf_del_extmark(0, ns, marks[3])
- feed('u')
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 })
- -- undo does NOT restore deleted marks
- eq(2, #rv)
- feed('<c-r>')
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 })
- eq(2, #rv)
- end)
- it('undo and redo of marks deleted during edits', function()
- -- test extmark_adjust
- feed('A<cr>12345<esc>')
- set_extmark(ns, marks[1], 1, 2)
- feed('dd')
- check_undo_redo(ns, marks[1], 1, 2, 1, 0)
- end)
- it('namespaces work properly', function()
- local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
- eq(1, rv)
- rv = set_extmark(ns2, marks[1], positions[1][1], positions[1][2])
- eq(1, rv)
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 })
- eq(1, #rv)
- rv = get_extmarks(ns2, { 0, 0 }, { -1, -1 })
- eq(1, #rv)
- -- Set more marks for testing the ranges
- set_extmark(ns, marks[2], positions[2][1], positions[2][2])
- set_extmark(ns, marks[3], positions[3][1], positions[3][2])
- set_extmark(ns2, marks[2], positions[2][1], positions[2][2])
- set_extmark(ns2, marks[3], positions[3][1], positions[3][2])
- -- get_next (limit set)
- rv = get_extmarks(ns, { 0, 0 }, positions[2], { limit = 1 })
- eq(1, #rv)
- rv = get_extmarks(ns2, { 0, 0 }, positions[2], { limit = 1 })
- eq(1, #rv)
- -- get_prev (limit set)
- rv = get_extmarks(ns, positions[1], { 0, 0 }, { limit = 1 })
- eq(1, #rv)
- rv = get_extmarks(ns2, positions[1], { 0, 0 }, { limit = 1 })
- eq(1, #rv)
- -- get_next (no limit)
- rv = get_extmarks(ns, positions[1], positions[2])
- eq(2, #rv)
- rv = get_extmarks(ns2, positions[1], positions[2])
- eq(2, #rv)
- -- get_prev (no limit)
- rv = get_extmarks(ns, positions[2], positions[1])
- eq(2, #rv)
- rv = get_extmarks(ns2, positions[2], positions[1])
- eq(2, #rv)
- api.nvim_buf_del_extmark(0, ns, marks[1])
- rv = get_extmarks(ns, { 0, 0 }, { -1, -1 })
- eq(2, #rv)
- api.nvim_buf_del_extmark(0, ns2, marks[1])
- rv = get_extmarks(ns2, { 0, 0 }, { -1, -1 })
- eq(2, #rv)
- end)
- it('mark set can create unique identifiers', function()
- -- create mark with id 1
- eq(1, set_extmark(ns, 1, positions[1][1], positions[1][2]))
- -- ask for unique id, it should be the next one, i e 2
- eq(2, set_extmark(ns, 0, positions[1][1], positions[1][2]))
- eq(3, set_extmark(ns, 3, positions[2][1], positions[2][2]))
- eq(4, set_extmark(ns, 0, positions[1][1], positions[1][2]))
- -- mixing manual and allocated id:s are not recommended, but it should
- -- do something reasonable
- eq(6, set_extmark(ns, 6, positions[2][1], positions[2][2]))
- eq(7, set_extmark(ns, 0, positions[1][1], positions[1][2]))
- eq(8, set_extmark(ns, 0, positions[1][1], positions[1][2]))
- end)
- it('auto indenting with enter works', function()
- -- op_reindent in ops.c
- feed(':set cindent<cr><esc>')
- feed(':set autoindent<cr><esc>')
- feed(':set shiftwidth=2<cr><esc>')
- feed('0iint <esc>A {1M1<esc>b<esc>')
- -- Set the mark on the M, should move..
- set_extmark(ns, marks[1], 0, 12)
- -- Set the mark before the cursor, should stay there
- set_extmark(ns, marks[2], 0, 10)
- feed('i<cr><esc>')
- local rv = get_extmark_by_id(ns, marks[1])
- eq({ 1, 3 }, rv)
- rv = get_extmark_by_id(ns, marks[2])
- eq({ 0, 10 }, rv)
- check_undo_redo(ns, marks[1], 0, 12, 1, 3)
- end)
- it('auto indenting entire line works', function()
- feed(':set cindent<cr><esc>')
- feed(':set autoindent<cr><esc>')
- feed(':set shiftwidth=2<cr><esc>')
- -- <c-f> will force an indent of 2
- feed('0iint <esc>A {<cr><esc>0i1M1<esc>')
- set_extmark(ns, marks[1], 1, 1)
- feed('0i<c-f><esc>')
- local rv = get_extmark_by_id(ns, marks[1])
- eq({ 1, 3 }, rv)
- check_undo_redo(ns, marks[1], 1, 1, 1, 3)
- -- now check when cursor at eol
- feed('uA<c-f><esc>')
- rv = get_extmark_by_id(ns, marks[1])
- eq({ 1, 3 }, rv)
- end)
- it('removing auto indenting with <C-D> works', function()
- feed(':set cindent<cr><esc>')
- feed(':set autoindent<cr><esc>')
- feed(':set shiftwidth=2<cr><esc>')
- feed('0i<tab><esc>')
- set_extmark(ns, marks[1], 0, 3)
- feed('bi<c-d><esc>')
- local rv = get_extmark_by_id(ns, marks[1])
- eq({ 0, 1 }, rv)
- check_undo_redo(ns, marks[1], 0, 3, 0, 1)
- -- check when cursor at eol
- feed('uA<c-d><esc>')
- rv = get_extmark_by_id(ns, marks[1])
- eq({ 0, 1 }, rv)
- end)
- it('indenting multiple lines with = works', function()
- feed(':set cindent<cr><esc>')
- feed(':set autoindent<cr><esc>')
- feed(':set shiftwidth=2<cr><esc>')
- feed('0iint <esc>A {<cr><bs>1M1<cr><bs>2M2<esc>')
- set_extmark(ns, marks[1], 1, 1)
- set_extmark(ns, marks[2], 2, 1)
- feed('=gg')
- check_undo_redo(ns, marks[1], 1, 1, 1, 3)
- check_undo_redo(ns, marks[2], 2, 1, 2, 5)
- end)
- it('substitutes by deleting inside the replace matches', function()
- -- do_sub in ex_cmds.c
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- feed(':s/34/xx<cr>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 4)
- check_undo_redo(ns, marks[2], 0, 3, 0, 4)
- end)
- it('substitutes when insert text > deleted', function()
- -- do_sub in ex_cmds.c
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- feed(':s/34/xxx<cr>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 5)
- check_undo_redo(ns, marks[2], 0, 3, 0, 5)
- end)
- it('substitutes when marks around eol', function()
- -- do_sub in ex_cmds.c
- set_extmark(ns, marks[1], 0, 4)
- set_extmark(ns, marks[2], 0, 5)
- feed(':s/5/xxx<cr>')
- check_undo_redo(ns, marks[1], 0, 4, 0, 7)
- check_undo_redo(ns, marks[2], 0, 5, 0, 7)
- end)
- it('substitutes over range insert text > deleted', function()
- -- do_sub in ex_cmds.c
- feed('A<cr>x34xx<esc>')
- feed('A<cr>xxx34<esc>')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 1, 1)
- set_extmark(ns, marks[3], 2, 4)
- feed(':1,3s/34/xxx<cr><esc>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 5)
- check_undo_redo(ns, marks[2], 1, 1, 1, 4)
- check_undo_redo(ns, marks[3], 2, 4, 2, 6)
- end)
- it('substitutes multiple matches in a line', function()
- -- do_sub in ex_cmds.c
- feed('ddi3x3x3<esc>')
- set_extmark(ns, marks[1], 0, 0)
- set_extmark(ns, marks[2], 0, 2)
- set_extmark(ns, marks[3], 0, 4)
- feed(':s/3/yy/g<cr><esc>')
- check_undo_redo(ns, marks[1], 0, 0, 0, 2)
- check_undo_redo(ns, marks[2], 0, 2, 0, 5)
- check_undo_redo(ns, marks[3], 0, 4, 0, 8)
- end)
- it('substitutes over multiple lines with newline in pattern', function()
- feed('A<cr>67890<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 3)
- set_extmark(ns, marks[2], 0, 4)
- set_extmark(ns, marks[3], 1, 0)
- set_extmark(ns, marks[4], 1, 5)
- set_extmark(ns, marks[5], 2, 0)
- feed([[:1,2s:5\n:5 <cr>]])
- check_undo_redo(ns, marks[1], 0, 3, 0, 3)
- check_undo_redo(ns, marks[2], 0, 4, 0, 6)
- check_undo_redo(ns, marks[3], 1, 0, 0, 6)
- check_undo_redo(ns, marks[4], 1, 5, 0, 11)
- check_undo_redo(ns, marks[5], 2, 0, 1, 0)
- end)
- it('inserting', function()
- feed('A<cr>67890<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 3)
- set_extmark(ns, marks[2], 0, 4)
- set_extmark(ns, marks[3], 1, 0)
- set_extmark(ns, marks[4], 1, 5)
- set_extmark(ns, marks[5], 2, 0)
- set_extmark(ns, marks[6], 1, 2)
- feed([[:1,2s:5\n67:X<cr>]])
- check_undo_redo(ns, marks[1], 0, 3, 0, 3)
- check_undo_redo(ns, marks[2], 0, 4, 0, 5)
- check_undo_redo(ns, marks[3], 1, 0, 0, 5)
- check_undo_redo(ns, marks[4], 1, 5, 0, 8)
- check_undo_redo(ns, marks[5], 2, 0, 1, 0)
- check_undo_redo(ns, marks[6], 1, 2, 0, 5)
- end)
- it('substitutes with multiple newlines in pattern', function()
- feed('A<cr>67890<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 4)
- set_extmark(ns, marks[2], 0, 5)
- set_extmark(ns, marks[3], 1, 0)
- set_extmark(ns, marks[4], 1, 5)
- set_extmark(ns, marks[5], 2, 0)
- feed([[:1,2s:\n.*\n:X<cr>]])
- check_undo_redo(ns, marks[1], 0, 4, 0, 4)
- check_undo_redo(ns, marks[2], 0, 5, 0, 6)
- check_undo_redo(ns, marks[3], 1, 0, 0, 6)
- check_undo_redo(ns, marks[4], 1, 5, 0, 6)
- check_undo_redo(ns, marks[5], 2, 0, 0, 6)
- end)
- it('substitutes over multiple lines with replace in substitution', function()
- feed('A<cr>67890<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 1)
- set_extmark(ns, marks[2], 0, 2)
- set_extmark(ns, marks[3], 0, 4)
- set_extmark(ns, marks[4], 1, 0)
- set_extmark(ns, marks[5], 2, 0)
- feed([[:1,2s:3:\r<cr>]])
- check_undo_redo(ns, marks[1], 0, 1, 0, 1)
- check_undo_redo(ns, marks[2], 0, 2, 1, 0)
- check_undo_redo(ns, marks[3], 0, 4, 1, 1)
- check_undo_redo(ns, marks[4], 1, 0, 2, 0)
- check_undo_redo(ns, marks[5], 2, 0, 3, 0)
- feed('u')
- feed([[:1,2s:3:\rxx<cr>]])
- eq({ 1, 3 }, get_extmark_by_id(ns, marks[3]))
- end)
- it('substitutes over multiple lines with replace in substitution', function()
- feed('A<cr>x3<cr>xx<esc>')
- set_extmark(ns, marks[1], 1, 0)
- set_extmark(ns, marks[2], 1, 1)
- set_extmark(ns, marks[3], 1, 2)
- feed([[:2,2s:3:\r<cr>]])
- check_undo_redo(ns, marks[1], 1, 0, 1, 0)
- check_undo_redo(ns, marks[2], 1, 1, 2, 0)
- check_undo_redo(ns, marks[3], 1, 2, 2, 0)
- end)
- it('substitutes over multiple lines with replace in substitution', function()
- feed('A<cr>x3<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 1)
- set_extmark(ns, marks[2], 0, 2)
- set_extmark(ns, marks[3], 0, 4)
- set_extmark(ns, marks[4], 1, 1)
- set_extmark(ns, marks[5], 2, 0)
- feed([[:1,2s:3:\r<cr>]])
- check_undo_redo(ns, marks[1], 0, 1, 0, 1)
- check_undo_redo(ns, marks[2], 0, 2, 1, 0)
- check_undo_redo(ns, marks[3], 0, 4, 1, 1)
- check_undo_redo(ns, marks[4], 1, 1, 3, 0)
- check_undo_redo(ns, marks[5], 2, 0, 4, 0)
- feed('u')
- feed([[:1,2s:3:\rxx<cr>]])
- check_undo_redo(ns, marks[3], 0, 4, 1, 3)
- end)
- it('substitutes with newline in match and sub, delta is 0', function()
- feed('A<cr>67890<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 3)
- set_extmark(ns, marks[2], 0, 4)
- set_extmark(ns, marks[3], 0, 5)
- set_extmark(ns, marks[4], 1, 0)
- set_extmark(ns, marks[5], 1, 5)
- set_extmark(ns, marks[6], 2, 0)
- feed([[:1,1s:5\n:\r<cr>]])
- check_undo_redo(ns, marks[1], 0, 3, 0, 3)
- check_undo_redo(ns, marks[2], 0, 4, 1, 0)
- check_undo_redo(ns, marks[3], 0, 5, 1, 0)
- check_undo_redo(ns, marks[4], 1, 0, 1, 0)
- check_undo_redo(ns, marks[5], 1, 5, 1, 5)
- check_undo_redo(ns, marks[6], 2, 0, 2, 0)
- end)
- it('substitutes with newline in match and sub, delta > 0', function()
- feed('A<cr>67890<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 3)
- set_extmark(ns, marks[2], 0, 4)
- set_extmark(ns, marks[3], 0, 5)
- set_extmark(ns, marks[4], 1, 0)
- set_extmark(ns, marks[5], 1, 5)
- set_extmark(ns, marks[6], 2, 0)
- feed([[:1,1s:5\n:\r\r<cr>]])
- check_undo_redo(ns, marks[1], 0, 3, 0, 3)
- check_undo_redo(ns, marks[2], 0, 4, 2, 0)
- check_undo_redo(ns, marks[3], 0, 5, 2, 0)
- check_undo_redo(ns, marks[4], 1, 0, 2, 0)
- check_undo_redo(ns, marks[5], 1, 5, 2, 5)
- check_undo_redo(ns, marks[6], 2, 0, 3, 0)
- end)
- it('substitutes with newline in match and sub, delta < 0', function()
- feed('A<cr>67890<cr>xx<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 3)
- set_extmark(ns, marks[2], 0, 4)
- set_extmark(ns, marks[3], 0, 5)
- set_extmark(ns, marks[4], 1, 0)
- set_extmark(ns, marks[5], 1, 5)
- set_extmark(ns, marks[6], 2, 1)
- set_extmark(ns, marks[7], 3, 0)
- feed([[:1,2s:5\n.*\n:\r<cr>]])
- check_undo_redo(ns, marks[1], 0, 3, 0, 3)
- check_undo_redo(ns, marks[2], 0, 4, 1, 0)
- check_undo_redo(ns, marks[3], 0, 5, 1, 0)
- check_undo_redo(ns, marks[4], 1, 0, 1, 0)
- check_undo_redo(ns, marks[5], 1, 5, 1, 0)
- check_undo_redo(ns, marks[6], 2, 1, 1, 1)
- check_undo_redo(ns, marks[7], 3, 0, 2, 0)
- end)
- it('substitutes with backrefs, newline inserted into sub', function()
- feed('A<cr>67890<cr>xx<cr>xx<esc>')
- set_extmark(ns, marks[1], 0, 3)
- set_extmark(ns, marks[2], 0, 4)
- set_extmark(ns, marks[3], 0, 5)
- set_extmark(ns, marks[4], 1, 0)
- set_extmark(ns, marks[5], 1, 5)
- set_extmark(ns, marks[6], 2, 0)
- feed([[:1,1s:5\(\n\):\0\1<cr>]])
- check_undo_redo(ns, marks[1], 0, 3, 0, 3)
- check_undo_redo(ns, marks[2], 0, 4, 2, 0)
- check_undo_redo(ns, marks[3], 0, 5, 2, 0)
- check_undo_redo(ns, marks[4], 1, 0, 2, 0)
- check_undo_redo(ns, marks[5], 1, 5, 2, 5)
- check_undo_redo(ns, marks[6], 2, 0, 3, 0)
- end)
- it('substitutes a ^', function()
- set_extmark(ns, marks[1], 0, 0)
- set_extmark(ns, marks[2], 0, 1)
- feed([[:s:^:x<cr>]])
- check_undo_redo(ns, marks[1], 0, 0, 0, 1)
- check_undo_redo(ns, marks[2], 0, 1, 0, 2)
- end)
- it('using <c-a> without increase in order of magnitude', function()
- -- do_addsub in ops.c
- feed('ddiabc998xxx<esc>Tc')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 0, 5)
- set_extmark(ns, marks[4], 0, 6)
- set_extmark(ns, marks[5], 0, 7)
- feed('<c-a>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- check_undo_redo(ns, marks[2], 0, 3, 0, 6)
- check_undo_redo(ns, marks[3], 0, 5, 0, 6)
- check_undo_redo(ns, marks[4], 0, 6, 0, 6)
- check_undo_redo(ns, marks[5], 0, 7, 0, 7)
- end)
- it('using <c-a> when increase in order of magnitude', function()
- -- do_addsub in ops.c
- feed('ddiabc999xxx<esc>Tc')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 0, 5)
- set_extmark(ns, marks[4], 0, 6)
- set_extmark(ns, marks[5], 0, 7)
- feed('<c-a>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- check_undo_redo(ns, marks[2], 0, 3, 0, 7)
- check_undo_redo(ns, marks[3], 0, 5, 0, 7)
- check_undo_redo(ns, marks[4], 0, 6, 0, 7)
- check_undo_redo(ns, marks[5], 0, 7, 0, 8)
- end)
- it('using <c-a> when negative and without decrease in order of magnitude', function()
- feed('ddiabc-999xxx<esc>T-')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 0, 6)
- set_extmark(ns, marks[4], 0, 7)
- set_extmark(ns, marks[5], 0, 8)
- feed('<c-a>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- check_undo_redo(ns, marks[2], 0, 3, 0, 7)
- check_undo_redo(ns, marks[3], 0, 6, 0, 7)
- check_undo_redo(ns, marks[4], 0, 7, 0, 7)
- check_undo_redo(ns, marks[5], 0, 8, 0, 8)
- end)
- it('using <c-a> when negative and decrease in order of magnitude', function()
- feed('ddiabc-1000xxx<esc>T-')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 0, 7)
- set_extmark(ns, marks[4], 0, 8)
- set_extmark(ns, marks[5], 0, 9)
- feed('<c-a>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- check_undo_redo(ns, marks[2], 0, 3, 0, 7)
- check_undo_redo(ns, marks[3], 0, 7, 0, 7)
- check_undo_redo(ns, marks[4], 0, 8, 0, 7)
- check_undo_redo(ns, marks[5], 0, 9, 0, 8)
- end)
- it('using <c-x> without decrease in order of magnitude', function()
- -- do_addsub in ops.c
- feed('ddiabc999xxx<esc>Tc')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 0, 5)
- set_extmark(ns, marks[4], 0, 6)
- set_extmark(ns, marks[5], 0, 7)
- feed('<c-x>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- check_undo_redo(ns, marks[2], 0, 3, 0, 6)
- check_undo_redo(ns, marks[3], 0, 5, 0, 6)
- check_undo_redo(ns, marks[4], 0, 6, 0, 6)
- check_undo_redo(ns, marks[5], 0, 7, 0, 7)
- end)
- it('using <c-x> when decrease in order of magnitude', function()
- -- do_addsub in ops.c
- feed('ddiabc1000xxx<esc>Tc')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 0, 6)
- set_extmark(ns, marks[4], 0, 7)
- set_extmark(ns, marks[5], 0, 8)
- feed('<c-x>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- check_undo_redo(ns, marks[2], 0, 3, 0, 6)
- check_undo_redo(ns, marks[3], 0, 6, 0, 6)
- check_undo_redo(ns, marks[4], 0, 7, 0, 6)
- check_undo_redo(ns, marks[5], 0, 8, 0, 7)
- end)
- it('using <c-x> when negative and without increase in order of magnitude', function()
- feed('ddiabc-998xxx<esc>T-')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 0, 6)
- set_extmark(ns, marks[4], 0, 7)
- set_extmark(ns, marks[5], 0, 8)
- feed('<c-x>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- check_undo_redo(ns, marks[2], 0, 3, 0, 7)
- check_undo_redo(ns, marks[3], 0, 6, 0, 7)
- check_undo_redo(ns, marks[4], 0, 7, 0, 7)
- check_undo_redo(ns, marks[5], 0, 8, 0, 8)
- end)
- it('using <c-x> when negative and increase in order of magnitude', function()
- feed('ddiabc-999xxx<esc>T-')
- set_extmark(ns, marks[1], 0, 2)
- set_extmark(ns, marks[2], 0, 3)
- set_extmark(ns, marks[3], 0, 6)
- set_extmark(ns, marks[4], 0, 7)
- set_extmark(ns, marks[5], 0, 8)
- feed('<c-x>')
- check_undo_redo(ns, marks[1], 0, 2, 0, 2)
- check_undo_redo(ns, marks[2], 0, 3, 0, 8)
- check_undo_redo(ns, marks[3], 0, 6, 0, 8)
- check_undo_redo(ns, marks[4], 0, 7, 0, 8)
- check_undo_redo(ns, marks[5], 0, 8, 0, 9)
- end)
- it('throws consistent error codes', function()
- local ns_invalid = ns2 + 1
- eq(
- "Invalid 'ns_id': 3",
- pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2])
- )
- eq("Invalid 'ns_id': 3", pcall_err(api.nvim_buf_del_extmark, 0, ns_invalid, marks[1]))
- eq("Invalid 'ns_id': 3", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
- eq("Invalid 'ns_id': 3", pcall_err(get_extmark_by_id, ns_invalid, marks[1]))
- end)
- it('when col = line-length, set the mark on eol', function()
- set_extmark(ns, marks[1], 0, -1)
- local rv = get_extmark_by_id(ns, marks[1])
- eq({ 0, init_text:len() }, rv)
- -- Test another
- set_extmark(ns, marks[1], 0, -1)
- rv = get_extmark_by_id(ns, marks[1])
- eq({ 0, init_text:len() }, rv)
- end)
- it('when col = line-length, set the mark on eol', function()
- local invalid_col = init_text:len() + 1
- eq("Invalid 'col': out of range", pcall_err(set_extmark, ns, marks[1], 0, invalid_col))
- end)
- it('fails when line > line_count', function()
- local invalid_col = init_text:len() + 1
- local invalid_lnum = 3
- eq(
- "Invalid 'line': out of range",
- pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col)
- )
- eq({}, get_extmark_by_id(ns, marks[1]))
- end)
- it('bug from check_col in extmark_set', function()
- -- This bug was caused by extmark_set always using check_col. check_col
- -- always uses the current buffer. This wasn't working during undo so we
- -- now use check_col and check_lnum only when they are required.
- feed('A<cr>67890<cr>xx<esc>')
- feed('A<cr>12345<cr>67890<cr>xx<esc>')
- set_extmark(ns, marks[1], 3, 4)
- feed([[:1,5s:5\n:5 <cr>]])
- check_undo_redo(ns, marks[1], 3, 4, 2, 6)
- end)
- it('in read-only buffer', function()
- command('view! runtime/doc/help.txt')
- eq(true, api.nvim_get_option_value('ro', {}))
- local id = set_extmark(ns, 0, 0, 2)
- eq({ { id, 0, 2 } }, get_extmarks(ns, 0, -1))
- end)
- it('can set a mark to other buffer', function()
- local buf = request('nvim_create_buf', 0, 1)
- request('nvim_buf_set_lines', buf, 0, -1, 1, { '', '' })
- local id = api.nvim_buf_set_extmark(buf, ns, 1, 0, {})
- eq({ { id, 1, 0 } }, api.nvim_buf_get_extmarks(buf, ns, 0, -1, {}))
- end)
- it('does not crash with append/delete/undo sequence', function()
- exec([[
- let ns = nvim_create_namespace('myplugin')
- call nvim_buf_set_extmark(0, ns, 0, 0, {})
- call append(0, '')
- %delete
- undo]])
- assert_alive()
- end)
- it('works with left and right gravity', function()
- -- right gravity should move with inserted text, while
- -- left gravity should stay in place.
- api.nvim_buf_set_extmark(0, ns, 0, 5, { right_gravity = false })
- api.nvim_buf_set_extmark(0, ns, 0, 5, { right_gravity = true })
- feed([[Aasdfasdf]])
- eq({ { 1, 0, 5 }, { 2, 0, 13 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {}))
- -- but both move when text is inserted before
- feed([[<esc>Iasdf<esc>]])
- -- eq({}, api.nvim_buf_get_lines(0, 0, -1, true))
- eq({ { 1, 0, 9 }, { 2, 0, 17 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {}))
- -- clear text
- api.nvim_buf_set_text(0, 0, 0, 0, 17, {})
- -- handles set_text correctly as well
- eq({ { 1, 0, 0 }, { 2, 0, 0 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {}))
- api.nvim_buf_set_text(0, 0, 0, 0, 0, { 'asdfasdf' })
- eq({ { 1, 0, 0 }, { 2, 0, 8 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {}))
- feed('u')
- -- handles pasting
- exec([[let @a='asdfasdf']])
- feed([["ap]])
- eq({ { 1, 0, 0 }, { 2, 0, 8 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {}))
- end)
- it('can accept "end_row" or "end_line" #16548', function()
- set_extmark(ns, marks[1], 0, 0, {
- end_col = 0,
- end_line = 1,
- })
- eq({
- {
- 1,
- 0,
- 0,
- {
- ns_id = 1,
- end_col = 0,
- end_row = 1,
- right_gravity = true,
- end_right_gravity = false,
- },
- },
- }, get_extmarks(ns, 0, -1, { details = true }))
- end)
- it('in prompt buffer', function()
- feed('dd')
- local id = set_extmark(ns, marks[1], 0, 0, {})
- api.nvim_set_option_value('buftype', 'prompt', {})
- feed('i<esc>')
- eq({ { id, 0, 2 } }, get_extmarks(ns, 0, -1))
- end)
- it('can get details', function()
- set_extmark(ns, marks[1], 0, 0, {
- conceal = 'c',
- cursorline_hl_group = 'Statement',
- end_col = 0,
- end_right_gravity = true,
- end_row = 1,
- hl_eol = true,
- hl_group = 'String',
- hl_mode = 'blend',
- line_hl_group = 'Statement',
- number_hl_group = 'Statement',
- priority = 0,
- right_gravity = false,
- sign_hl_group = 'Statement',
- sign_text = '>>',
- spell = true,
- virt_lines = {
- { { 'lines', 'Macro' }, { '???' }, { ';;;', '' } },
- { { 'stack', { 'Type', 'Search' } }, { '!!!' } },
- },
- virt_lines_above = true,
- virt_lines_leftcol = true,
- virt_text = { { 'text', 'Macro' }, { '???' }, { 'stack', { 'Type', 'Search' } } },
- virt_text_hide = true,
- virt_text_pos = 'right_align',
- })
- set_extmark(ns, marks[2], 0, 0, {
- priority = 0,
- virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } },
- virt_text_win_col = 1,
- })
- eq({
- 0,
- 0,
- {
- conceal = 'c',
- cursorline_hl_group = 'Statement',
- end_col = 0,
- end_right_gravity = true,
- end_row = 1,
- hl_eol = true,
- hl_group = 'String',
- hl_mode = 'blend',
- line_hl_group = 'Statement',
- ns_id = 1,
- number_hl_group = 'Statement',
- priority = 0,
- right_gravity = false,
- sign_hl_group = 'Statement',
- sign_text = '>>',
- spell = true,
- virt_lines = {
- { { 'lines', 'Macro' }, { '???' }, { ';;;', '' } },
- { { 'stack', { 'Type', 'Search' } }, { '!!!' } },
- },
- virt_lines_above = true,
- virt_lines_leftcol = true,
- virt_text = { { 'text', 'Macro' }, { '???' }, { 'stack', { 'Type', 'Search' } } },
- virt_text_repeat_linebreak = false,
- virt_text_hide = true,
- virt_text_pos = 'right_align',
- },
- }, get_extmark_by_id(ns, marks[1], { details = true }))
- eq({
- 0,
- 0,
- {
- ns_id = 1,
- right_gravity = true,
- priority = 0,
- virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } },
- virt_text_repeat_linebreak = false,
- virt_text_hide = false,
- virt_text_pos = 'win_col',
- virt_text_win_col = 1,
- },
- }, get_extmark_by_id(ns, marks[2], { details = true }))
- set_extmark(ns, marks[3], 0, 0, { cursorline_hl_group = 'Statement' })
- eq({
- 0,
- 0,
- {
- ns_id = 1,
- cursorline_hl_group = 'Statement',
- priority = 4096,
- right_gravity = true,
- },
- }, get_extmark_by_id(ns, marks[3], { details = true }))
- set_extmark(ns, marks[4], 0, 0, {
- end_col = 1,
- conceal = 'a',
- spell = true,
- })
- eq({
- 0,
- 0,
- {
- conceal = 'a',
- end_col = 1,
- end_right_gravity = false,
- end_row = 0,
- ns_id = 1,
- right_gravity = true,
- spell = true,
- },
- }, get_extmark_by_id(ns, marks[4], { details = true }))
- set_extmark(ns, marks[5], 0, 0, {
- end_col = 1,
- spell = false,
- })
- eq({
- 0,
- 0,
- {
- end_col = 1,
- end_right_gravity = false,
- end_row = 0,
- ns_id = 1,
- right_gravity = true,
- spell = false,
- },
- }, get_extmark_by_id(ns, marks[5], { details = true }))
- api.nvim_buf_clear_namespace(0, ns, 0, -1)
- -- legacy sign mark includes sign name
- command('sign define sign1 text=s1 texthl=Title linehl=LineNR numhl=Normal culhl=CursorLine')
- command('sign place 1 name=sign1 line=1')
- eq({
- {
- 1,
- 0,
- 0,
- {
- cursorline_hl_group = 'CursorLine',
- invalidate = true,
- line_hl_group = 'LineNr',
- ns_id = 0,
- number_hl_group = 'Normal',
- priority = 10,
- right_gravity = true,
- sign_hl_group = 'Title',
- sign_name = 'sign1',
- sign_text = 's1',
- undo_restore = false,
- },
- },
- }, get_extmarks(-1, 0, -1, { details = true }))
- end)
- it('can get marks from anonymous namespaces', function()
- ns = request('nvim_create_namespace', '')
- ns2 = request('nvim_create_namespace', '')
- set_extmark(ns, 1, 0, 0, {})
- set_extmark(ns2, 2, 1, 0, {})
- eq({
- { 1, 0, 0, { ns_id = ns, right_gravity = true } },
- { 2, 1, 0, { ns_id = ns2, right_gravity = true } },
- }, get_extmarks(-1, 0, -1, { details = true }))
- end)
- it('can filter by extmark properties', function()
- set_extmark(ns, 1, 0, 0, {})
- set_extmark(ns, 2, 0, 0, { hl_group = 'Normal' })
- set_extmark(ns, 3, 0, 0, { sign_text = '>>' })
- set_extmark(ns, 4, 0, 0, { virt_text = { { 'text', 'Normal' } } })
- set_extmark(ns, 5, 0, 0, { virt_lines = { { { 'line', 'Normal' } } } })
- eq(5, #get_extmarks(-1, 0, -1, {}))
- eq({ { 2, 0, 0 } }, get_extmarks(-1, 0, -1, { type = 'highlight' }))
- eq({ { 3, 0, 0 } }, get_extmarks(-1, 0, -1, { type = 'sign' }))
- eq({ { 4, 0, 0 } }, get_extmarks(-1, 0, -1, { type = 'virt_text' }))
- eq({ { 5, 0, 0 } }, get_extmarks(-1, 0, -1, { type = 'virt_lines' }))
- end)
- it('invalidated marks are deleted', function()
- screen = Screen.new(40, 6)
- feed('dd6iaaa bbb ccc<CR><ESC>gg')
- api.nvim_set_option_value('signcolumn', 'auto:2', {})
- set_extmark(ns, 1, 0, 0, { invalidate = true, sign_text = 'S1', end_row = 1 })
- set_extmark(ns, 2, 1, 0, { invalidate = true, sign_text = 'S2', end_row = 2 })
- -- mark with invalidate is removed
- command('d2')
- screen:expect([[
- S2^aaa bbb ccc |
- {7: }aaa bbb ccc |*3
- {7: } |
- |
- ]])
- -- mark is restored with undo_restore == true
- command('silent undo')
- screen:expect([[
- S1{7: }^aaa bbb ccc |
- S2S1aaa bbb ccc |
- S2{7: }aaa bbb ccc |
- {7: }aaa bbb ccc |*2
- |
- ]])
- -- decor is not removed twice
- command('d3')
- api.nvim_buf_del_extmark(0, ns, 1)
- command('silent undo')
- -- mark is deleted with undo_restore == false
- set_extmark(ns, 1, 0, 0, { invalidate = true, undo_restore = false, sign_text = 'S1' })
- set_extmark(ns, 2, 1, 0, { invalidate = true, undo_restore = false, sign_text = 'S2' })
- command('1d 2')
- eq(0, #get_extmarks(-1, 0, -1, {}))
- -- mark is not removed when deleting bytes before the range
- set_extmark(ns, 3, 0, 4, {
- invalidate = true,
- undo_restore = true,
- hl_group = 'Error',
- end_col = 7,
- right_gravity = false,
- })
- feed('dw')
- eq(3, get_extmark_by_id(ns, 3, { details = true })[3].end_col)
- -- mark is not removed when deleting bytes at the start of the range
- feed('x')
- eq(2, get_extmark_by_id(ns, 3, { details = true })[3].end_col)
- -- mark is not removed when deleting bytes from the end of the range
- feed('lx')
- eq(1, get_extmark_by_id(ns, 3, { details = true })[3].end_col)
- -- mark is not removed when deleting bytes beyond end of the range
- feed('x')
- eq(1, get_extmark_by_id(ns, 3, { details = true })[3].end_col)
- -- mark is removed when all bytes in the range are deleted
- feed('hx')
- eq(true, get_extmark_by_id(ns, 3, { details = true })[3].invalid)
- -- mark is restored with undo_restore == true if pos did not change
- command('undo')
- eq(nil, get_extmark_by_id(ns, 3, { details = true })[3].invalid)
- -- multiline mark is not removed when start of its range is deleted
- set_extmark(ns, 4, 1, 4, {
- undo_restore = false,
- invalidate = true,
- hl_group = 'Error',
- end_col = 7,
- end_row = 3,
- })
- feed('ddDdd')
- eq({ 0, 0 }, get_extmark_by_id(ns, 4, {}))
- -- multiline mark is removed when entirety of its range is deleted
- feed('vj2ed')
- eq({}, get_extmark_by_id(ns, 4, {}))
- end)
- it('can set a URL', function()
- local url1 = 'https://example.com'
- local url2 = 'http://127.0.0.1'
- set_extmark(ns, 1, 0, 0, { url = url1, end_col = 3 })
- set_extmark(ns, 2, 0, 3, { url = url2, hl_group = 'Search', end_col = 5 })
- local extmarks = get_extmarks(ns, 0, -1, { details = true })
- eq(2, #extmarks)
- eq(url1, extmarks[1][4].url)
- eq(url2, extmarks[2][4].url)
- eq('Search', extmarks[2][4].hl_group)
- end)
- it('respects priority', function()
- screen = Screen.new(15, 10)
- set_extmark(ns, marks[1], 0, 0, {
- hl_group = 'Comment',
- end_col = 2,
- priority = 20,
- })
- -- Extmark defined after first extmark but has lower priority, first extmark "wins"
- set_extmark(ns, marks[2], 0, 0, {
- hl_group = 'String',
- end_col = 2,
- priority = 10,
- })
- screen:expect {
- grid = [[
- {1:12}34^5 |
- {2:~ }|*8
- |
- ]],
- attr_ids = {
- [1] = { foreground = Screen.colors.Blue1 },
- [2] = { foreground = Screen.colors.Blue1, bold = true },
- },
- }
- end)
- end)
- describe('Extmarks buffer api with many marks', function()
- local ns1
- local ns2
- local ns_marks = {}
- before_each(function()
- clear()
- ns1 = request('nvim_create_namespace', 'ns1')
- ns2 = request('nvim_create_namespace', 'ns2')
- ns_marks = { [ns1] = {}, [ns2] = {} }
- local lines = {}
- for i = 1, 30 do
- lines[#lines + 1] = string.rep('x ', i)
- end
- api.nvim_buf_set_lines(0, 0, -1, true, lines)
- local ns = ns1
- local q = 0
- for i = 0, 29 do
- for j = 0, i do
- local id = set_extmark(ns, 0, i, j)
- eq(nil, ns_marks[ns][id])
- ok(id > 0)
- ns_marks[ns][id] = { i, j }
- ns = ns1 + ns2 - ns
- q = q + 1
- end
- end
- eq(233, #ns_marks[ns1])
- eq(232, #ns_marks[ns2])
- end)
- local function get_marks(ns)
- local mark_list = get_extmarks(ns, 0, -1)
- local marks = {}
- for _, mark in ipairs(mark_list) do
- local id, row, col = unpack(mark)
- eq(nil, marks[id], 'duplicate mark')
- marks[id] = { row, col }
- end
- return marks
- end
- it('can get marks', function()
- eq(ns_marks[ns1], get_marks(ns1))
- eq(ns_marks[ns2], get_marks(ns2))
- end)
- it('can clear all marks in ns', function()
- api.nvim_buf_clear_namespace(0, ns1, 0, -1)
- eq({}, get_marks(ns1))
- eq(ns_marks[ns2], get_marks(ns2))
- api.nvim_buf_clear_namespace(0, ns2, 0, -1)
- eq({}, get_marks(ns1))
- eq({}, get_marks(ns2))
- end)
- it('can clear line range', function()
- api.nvim_buf_clear_namespace(0, ns1, 10, 20)
- for id, mark in pairs(ns_marks[ns1]) do
- if 10 <= mark[1] and mark[1] < 20 then
- ns_marks[ns1][id] = nil
- end
- end
- eq(ns_marks[ns1], get_marks(ns1))
- eq(ns_marks[ns2], get_marks(ns2))
- api.nvim_buf_clear_namespace(0, ns1, 0, 10)
- for id, mark in pairs(ns_marks[ns1]) do
- if mark[1] < 10 then
- ns_marks[ns1][id] = nil
- end
- end
- eq(ns_marks[ns1], get_marks(ns1))
- eq(ns_marks[ns2], get_marks(ns2))
- api.nvim_buf_clear_namespace(0, ns1, 20, -1)
- for id, mark in pairs(ns_marks[ns1]) do
- if mark[1] >= 20 then
- ns_marks[ns1][id] = nil
- end
- end
- eq(ns_marks[ns1], get_marks(ns1))
- eq(ns_marks[ns2], get_marks(ns2))
- end)
- it('can delete line', function()
- feed('10Gdd')
- for _, marks in pairs(ns_marks) do
- for id, mark in pairs(marks) do
- if mark[1] == 9 then
- marks[id] = { 9, 0 }
- elseif mark[1] >= 10 then
- mark[1] = mark[1] - 1
- end
- end
- end
- eq(ns_marks[ns1], get_marks(ns1))
- eq(ns_marks[ns2], get_marks(ns2))
- end)
- it('can delete lines', function()
- feed('10G10dd')
- for _, marks in pairs(ns_marks) do
- for id, mark in pairs(marks) do
- if 9 <= mark[1] and mark[1] < 19 then
- marks[id] = { 9, 0 }
- elseif mark[1] >= 19 then
- mark[1] = mark[1] - 10
- end
- end
- end
- eq(ns_marks[ns1], get_marks(ns1))
- eq(ns_marks[ns2], get_marks(ns2))
- end)
- it('can wipe buffer', function()
- command('bwipe!')
- eq({}, get_marks(ns1))
- eq({}, get_marks(ns2))
- end)
- end)
- describe('API/win_extmark', function()
- local screen
- local marks, line1, line2
- local ns
- before_each(function()
- -- Initialize some namespaces and insert text into a buffer
- marks = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
- line1 = 'non ui-watched line'
- line2 = 'ui-watched line'
- clear()
- insert(line1)
- feed('o<esc>')
- insert(line2)
- ns = request('nvim_create_namespace', 'extmark-ui')
- end)
- it('sends and only sends ui-watched marks to ui', function()
- screen = Screen.new(20, 4)
- -- should send this
- set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
- -- should not send this
- set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
- screen:expect({
- grid = [[
- non ui-watched line |
- ui-watched lin^e |
- {1:~ }|
- |
- ]],
- extmarks = {
- [2] = {
- -- positioned at the end of the 2nd line
- { 1000, ns, marks[1], 1, 16 },
- },
- },
- })
- end)
- it('sends multiple ui-watched marks to ui', function()
- screen = Screen.new(20, 4)
- feed('15A!<Esc>')
- -- should send all of these
- set_extmark(ns, marks[1], 1, 0, { ui_watched = true, virt_text_pos = 'overlay' })
- set_extmark(ns, marks[2], 1, 2, { ui_watched = true, virt_text_pos = 'overlay' })
- set_extmark(ns, marks[3], 1, 4, { ui_watched = true, virt_text_pos = 'overlay' })
- set_extmark(ns, marks[4], 1, 6, { ui_watched = true, virt_text_pos = 'overlay' })
- set_extmark(ns, marks[5], 1, 8, { ui_watched = true })
- screen:expect({
- grid = [[
- non ui-watched line |
- ui-watched line!!!!!|
- !!!!!!!!!^! |
- |
- ]],
- extmarks = {
- [2] = {
- -- notification from 1st call
- { 1000, ns, marks[1], 1, 0 },
- -- notifications from 2nd call
- { 1000, ns, marks[1], 1, 0 },
- { 1000, ns, marks[2], 1, 2 },
- -- notifications from 3rd call
- { 1000, ns, marks[1], 1, 0 },
- { 1000, ns, marks[2], 1, 2 },
- { 1000, ns, marks[3], 1, 4 },
- -- notifications from 4th call
- { 1000, ns, marks[1], 1, 0 },
- { 1000, ns, marks[2], 1, 2 },
- { 1000, ns, marks[3], 1, 4 },
- { 1000, ns, marks[4], 1, 6 },
- -- final
- -- overlay
- { 1000, ns, marks[1], 1, 0 },
- { 1000, ns, marks[2], 1, 2 },
- { 1000, ns, marks[3], 1, 4 },
- { 1000, ns, marks[4], 1, 6 },
- -- eol
- { 1000, ns, marks[5], 2, 11 },
- },
- },
- })
- end)
- it('updates ui-watched marks', function()
- screen = Screen.new(20, 4)
- -- should send this
- set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
- -- should not send this
- set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
- -- make some changes
- insert(' update')
- screen:expect({
- grid = [[
- non ui-watched line |
- ui-watched linupdat^e|
- e |
- |
- ]],
- extmarks = {
- [2] = {
- -- positioned at the end of the 2nd line
- { 1000, ns, marks[1], 1, 16 },
- -- updated and wrapped to 3rd line
- { 1000, ns, marks[1], 2, 2 },
- },
- },
- })
- feed('<c-e>')
- screen:expect({
- grid = [[
- ui-watched linupdat^e|
- e |
- {1:~ }|
- |
- ]],
- extmarks = {
- [2] = {
- -- positioned at the end of the 2nd line
- { 1000, ns, marks[1], 1, 16 },
- -- updated and wrapped to 3rd line
- { 1000, ns, marks[1], 2, 2 },
- -- scrolled up one line, should be handled by grid scroll
- },
- },
- })
- end)
- it('sends ui-watched to splits', function()
- screen = Screen.new(20, 8, { ext_multigrid = true })
- -- should send this
- set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
- -- should not send this
- set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
- command('split')
- screen:expect({
- grid = [[
- ## grid 1
- [4:--------------------]|*3
- {3:[No Name] [+] }|
- [2:--------------------]|*2
- {2:[No Name] [+] }|
- [3:--------------------]|
- ## grid 2
- non ui-watched line |
- ui-watched line |
- ## grid 3
- |
- ## grid 4
- non ui-watched line |
- ui-watched lin^e |
- {1:~ }|
- ]],
- extmarks = {
- [2] = {
- -- positioned at the end of the 2nd line
- { 1000, ns, marks[1], 1, 16 },
- -- updated after split
- { 1000, ns, marks[1], 1, 16 },
- },
- [4] = {
- -- only after split
- { 1001, ns, marks[1], 1, 16 },
- },
- },
- })
- -- make some changes
- insert(' update')
- screen:expect({
- grid = [[
- ## grid 1
- [4:--------------------]|*3
- {3:[No Name] [+] }|
- [2:--------------------]|*2
- {2:[No Name] [+] }|
- [3:--------------------]|
- ## grid 2
- non ui-watched line |
- ui-watched linupd{1:@@@}|
- ## grid 3
- |
- ## grid 4
- non ui-watched line |
- ui-watched linupdat^e|
- e |
- ]],
- extmarks = {
- [2] = {
- -- positioned at the end of the 2nd line
- { 1000, ns, marks[1], 1, 16 },
- -- updated after split
- { 1000, ns, marks[1], 1, 16 },
- },
- [4] = {
- { 1001, ns, marks[1], 1, 16 },
- -- updated
- { 1001, ns, marks[1], 2, 2 },
- },
- },
- })
- end)
- end)
|