local R = require 'u.renderer' local withbuf = loadfile './spec/withbuf.lua'() local function getlines() return vim.api.nvim_buf_get_lines(0, 0, -1, true) end describe('Renderer', function() it('should render text in an empty buffer', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { 'hello', ' ', 'world' } assert.are.same(getlines(), { 'hello world' }) end) end) it('should result in the correct text after repeated renders', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { 'hello', ' ', 'world' } assert.are.same(getlines(), { 'hello world' }) r:render { 'goodbye', ' ', 'world' } assert.are.same(getlines(), { 'goodbye world' }) r:render { 'hello', ' ', 'universe' } assert.are.same(getlines(), { 'hello universe' }) end) end) it('should handle tags correctly', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { R.h('text', { hl = 'HighlightGroup' }, 'hello '), R.h('text', { hl = 'HighlightGroup' }, 'world'), } assert.are.same(getlines(), { 'hello world' }) end) end) it('should reconcile added lines', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { 'line 1', '\n', 'line 2' } assert.are.same(getlines(), { 'line 1', 'line 2' }) -- Add a new line: r:render { 'line 1', '\n', 'line 2\n', 'line 3' } assert.are.same(getlines(), { 'line 1', 'line 2', 'line 3' }) end) end) it('should reconcile deleted lines', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { 'line 1', '\nline 2', '\nline 3' } assert.are.same(getlines(), { 'line 1', 'line 2', 'line 3' }) -- Remove a line: r:render { 'line 1', '\nline 3' } assert.are.same(getlines(), { 'line 1', 'line 3' }) end) end) it('should handle multiple nested elements', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { R.h('text', {}, { 'first line', }), '\n', R.h('text', {}, 'second line'), } assert.are.same(getlines(), { 'first line', 'second line' }) r:render { R.h('text', {}, 'updated first line'), '\n', R.h('text', {}, 'third line'), } assert.are.same(getlines(), { 'updated first line', 'third line' }) end) end) -- -- get_tags_at -- it('should return no extmarks for an empty buffer', function() withbuf({}, function() local r = R.Renderer.new(0) local pos_infos = r:get_tags_at { 0, 0 } assert.are.same(pos_infos, {}) end) end) it('should return correct extmark for a given position', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { R.h('text', { hl = 'HighlightGroup1' }, 'Hello'), R.h('text', { hl = 'HighlightGroup2' }, ' World'), } local pos_infos = r:get_tags_at { 0, 2 } assert.are.same(#pos_infos, 1) assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup1') assert.are.same(pos_infos[1].extmark.start, { 0, 0 }) assert.are.same(pos_infos[1].extmark.stop, { 0, 5 }) pos_infos = r:get_tags_at { 0, 4 } assert.are.same(#pos_infos, 1) assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup1') assert.are.same(pos_infos[1].extmark.start, { 0, 0 }) assert.are.same(pos_infos[1].extmark.stop, { 0, 5 }) pos_infos = r:get_tags_at { 0, 5 } assert.are.same(#pos_infos, 1) assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup2') assert.are.same(pos_infos[1].extmark.start, { 0, 5 }) assert.are.same(pos_infos[1].extmark.stop, { 0, 11 }) -- In insert mode, bounds are eagerly included: pos_infos = r:get_tags_at({ 0, 5 }, 'i') assert.are.same(#pos_infos, 2) assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup1') assert.are.same(pos_infos[2].tag.attributes.hl, 'HighlightGroup2') end) end) it('should return multiple extmarks for overlapping text', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { R.h('text', { hl = 'HighlightGroup1' }, { 'Hello', R.h( 'text', { hl = 'HighlightGroup2', extmark = { hl_group = 'HighlightGroup2' } }, ' World' ), }), } local pos_infos = r:get_tags_at { 0, 5 } assert.are.same(#pos_infos, 2) assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup2') assert.are.same(pos_infos[2].tag.attributes.hl, 'HighlightGroup1') end) end) it('repeated patch_lines calls should not change the buffer content', function() local lines = { [[{ {]], [[ bounds = {]], [[ start1 = { 1, 1 },]], [[ stop1 = { 4, 1 }]], [[ },]], [[ end_right_gravity = true,]], [[ id = 1,]], [[ ns_id = 623,]], [[ ns_name = "my.renderer:91",]], [[ right_gravity = false]], [[ } }]], [[]], } withbuf(lines, function() local Buffer = require 'u.buffer' R.Renderer.patch_lines(0, nil, lines) assert.are.same(Buffer.current():all():lines(), lines) R.Renderer.patch_lines(0, lines, lines) assert.are.same(Buffer.current():all():lines(), lines) R.Renderer.patch_lines(0, lines, lines) assert.are.same(Buffer.current():all():lines(), lines) end) end) it('should fire text-changed events', function() withbuf({}, function() local Buffer = require 'u.buffer' local buf = Buffer.current() local r = R.Renderer.new(0) local captured_changed_text = '' r:render { R.h('text', { on_change = function(txt) captured_changed_text = txt end, }, { 'one\n', 'two\n', 'three\n', }), } vim.fn.setreg('"', 'bleh') vim.cmd [[normal! ggVGp]] -- For some reason, the autocmd does not fire in the busted environment. -- We'll call the handler ourselves: r:_on_text_changed() assert.are.same(buf:all():text(), 'bleh') assert.are.same(captured_changed_text, 'bleh') end) end) it('should find tags by position', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { 'pre', R.h('text', { id = 'outer', }, { 'inner-pre', R.h('text', { id = 'inner', }, { 'inner-text', }), 'inner-post', }), 'post', } local tags = r:get_tags_at { 0, 11 } assert.are.same(#tags, 1) assert.are.same(tags[1].tag.attributes.id, 'outer') tags = r:get_tags_at { 0, 12 } assert.are.same(#tags, 2) assert.are.same(tags[1].tag.attributes.id, 'inner') assert.are.same(tags[2].tag.attributes.id, 'outer') end) end) it('should find tags by id', function() withbuf({}, function() local r = R.Renderer.new(0) r:render { R.h('text', { id = 'outer', }, { 'inner-pre', R.h('text', { id = 'inner', }, { 'inner-text', }), 'inner-post', }), 'post', } local bounds = r:get_tag_bounds 'outer' assert.are.same(bounds, { start = { 0, 0 }, stop = { 0, 29 } }) bounds = r:get_tag_bounds 'inner' assert.are.same(bounds, { start = { 0, 9 }, stop = { 0, 19 } }) end) end) end)