---@diagnostic disable: duplicate-set-field, need-check-nil, param-type-mismatch, assign-type-mismatch local edit = require 'bugabinga.pivi.edit' local activity = require 'bugabinga.pivi.activity' local temp_file = function( lines ) local path = vim.fs.normalize( vim.fn.tempname() .. '.txt' ) vim.fn.writefile( lines, path ) return path end local open = function( path ) vim.cmd( 'edit! ' .. vim.fn.fnameescape( path ) ) return vim.api.nvim_get_current_buf() end local lines_of = function( bufnr ) return vim.api.nvim_buf_get_lines( bufnr, 0, -1, false ) end describe( 'pivi edit', function() after_each( function() vim.cmd 'silent! %bwipeout!' activity.clear_trail() end ) it( 'finds the loaded buffer for a path', function() local path = temp_file { 'one', } local bufnr = open( path ) assert.equal( bufnr, edit.buffer_for( path ) ) assert.is_nil( edit.buffer_for( path .. '.absent' ) ) end ) it( 'prefers unsaved buffer content over disk content', function() local path = temp_file { 'on disk', } local bufnr = open( path ) vim.api.nvim_buf_set_lines( bufnr, 0, -1, false, { 'in buffer', } ) local content, source = edit.read( path ) assert.equal( 'in buffer', content ) assert.equal( 'buffer', source ) assert.same( { 'on disk', }, vim.fn.readfile( path ) ) end ) it( 'falls back to disk when no buffer is loaded', function() local path = temp_file { 'only on disk', } local content, source = edit.read( path ) assert.equal( 'only on disk', content ) assert.equal( 'disk', source ) end ) it( 'lands an edit in the loaded buffer without a reload', function() local path = temp_file { 'alpha', 'beta', 'gamma', } local bufnr = open( path ) local applied = edit.apply( bufnr, { { first = 1, last = 2, lines = { 'BETA', }, }, } ) assert.is_true( applied ) assert.same( { 'alpha', 'BETA', 'gamma', }, lines_of( bufnr ) ) assert.is_true( vim.bo[bufnr].modified ) end ) it( 'keeps a multi part edit inside one undo block', function() local path = temp_file { 'one', 'two', 'three', 'four', } local bufnr = open( path ) local before = lines_of( bufnr ) local applied, joined = edit.apply( bufnr, { { first = 0, last = 1, lines = { 'ONE', }, }, { first = 3, last = 4, lines = { 'FOUR', }, }, } ) assert.is_true( applied ) assert.is_true( joined ) assert.same( { 'ONE', 'two', 'three', 'FOUR', }, lines_of( bufnr ) ) vim.api.nvim_buf_call( bufnr, function() vim.cmd 'silent undo' end ) assert.same( before, lines_of( bufnr ) ) end ) it( 'still applies the edit when neovim refuses to join undo blocks', function() local path = temp_file { 'one', 'two', 'three', } local bufnr = open( path ) vim.api.nvim_buf_set_lines( bufnr, 0, 1, false, { 'scratch', } ) vim.api.nvim_buf_call( bufnr, function() vim.cmd 'silent undo' end ) local applied = edit.apply( bufnr, { { first = 0, last = 1, lines = { 'ONE', }, }, { first = 2, last = 3, lines = { 'THREE', }, }, } ) assert.is_true( applied ) assert.same( { 'ONE', 'two', 'THREE', }, lines_of( bufnr ) ) end ) it( 'marks every changed line with a navigable trail', function() local path = temp_file { 'one', 'two', 'three', } local bufnr = open( path ) edit.apply( bufnr, { { first = 1, last = 2, lines = { 'TWO', }, }, } ) assert.is_true( #activity.marks( bufnr ) > 0 ) assert.is_true( #activity.trail() > 0 ) end ) it( 'replaces one unique occurrence', function() local path = temp_file { 'local value = 1', 'return value', } local bufnr = open( path ) local applied = edit.replace( bufnr, 'local value = 1', 'local value = 2' ) assert.is_true( applied ) assert.same( { 'local value = 2', 'return value', }, lines_of( bufnr ) ) end ) it( 'marks only the lines a replacement actually touched', function() local path = temp_file { 'one', 'two', 'three', 'four', 'five', } local bufnr = open( path ) edit.replace( bufnr, 'three', 'THREE' ) assert.same( { { buffer = bufnr, first = 3, last = 3, }, }, activity.trail() ) assert.equal( 1, #activity.marks( bufnr ) ) end ) it( 'keeps a replacement undoable as one change', function() local path = temp_file { 'one', 'two', 'three', } local bufnr = open( path ) local before = lines_of( bufnr ) edit.replace( bufnr, 'two', 'TWO' ) vim.api.nvim_buf_call( bufnr, function() vim.cmd 'silent undo' end ) assert.same( before, lines_of( bufnr ) ) end ) it( 'rewrites only what differs when setting whole content', function() local path = temp_file { 'one', 'two', 'three', 'four', 'five', } local bufnr = open( path ) local applied = edit.set_content( bufnr, 'one\ntwo\nTHREE\nfour\nfive' ) assert.is_true( applied ) assert.same( { 'one', 'two', 'THREE', 'four', 'five', }, lines_of( bufnr ) ) assert.is_true( #activity.marks( bufnr ) < 5 ) end ) it( 'reports identical content as already applied without marking it', function() local path = temp_file { 'one', 'two', } local bufnr = open( path ) assert.is_true( edit.set_content( bufnr, 'one\ntwo' ) ) assert.equal( 0, #activity.marks( bufnr ) ) assert.is_false( vim.bo[bufnr].modified ) end ) it( 'sets content that grows and shrinks the buffer', function() local path = temp_file { 'one', 'two', } local bufnr = open( path ) assert.is_true( edit.set_content( bufnr, 'one\ninserted\ntwo\ntail' ) ) assert.same( { 'one', 'inserted', 'two', 'tail', }, lines_of( bufnr ) ) assert.is_true( edit.set_content( bufnr, 'only' ) ) assert.same( { 'only', }, lines_of( bufnr ) ) end ) it( 'refuses an ambiguous replacement', function() local path = temp_file { 'same', 'same', } local bufnr = open( path ) local applied, failure = edit.replace( bufnr, 'same', 'other' ) assert.is_false( applied ) assert.equal( 'text is not unique', failure ) assert.same( { 'same', 'same', }, lines_of( bufnr ) ) end ) it( 'refuses a replacement whose text is absent', function() local path = temp_file { 'one', } local bufnr = open( path ) local applied, failure = edit.replace( bufnr, 'missing', 'other' ) assert.is_false( applied ) assert.equal( 'text not found', failure ) end ) it( 'makes an agent edit visible to later filesystem readers', function() local path = temp_file { 'before', } local bufnr = open( path ) edit.apply( bufnr, { { first = 0, last = 1, lines = { 'after', }, }, } ) assert.same( { 'before', }, vim.fn.readfile( path ) ) assert.is_true( edit.persist( bufnr ) ) assert.same( { 'after', }, vim.fn.readfile( path ) ) assert.is_false( vim.bo[bufnr].modified ) end ) it( 'never persists a scratch buffer', function() local bufnr = vim.api.nvim_create_buf( false, true ) assert.is_false( edit.persist( bufnr ) ) end ) end )