ronantakizawa/codereview-bench huggingface dataset:
Repository: neovim/neovim
File: src/nvim/state.c (C)

Code before:

    int execute_result = s->execute(s, key);
    if (!execute_result) {
      break;
    } else if (execute_result == -1) {
      goto getkey;
    }
  }
}

/// process events on main_loop, but interrupt if input is available
///
/// This should be used to handle K_EVENT in states accepting input
/// otherwise bursts of events can block break checking indefinitely.
void state_handle_k_event(void)
{
  while (true) {
    Event event = multiqueue_get(main_loop.events);
    if (event.handler) {
      event.handler(event.argv);
    }

    if (multiqueue_empty(main_loop.events)) {
      // don't breakcheck before return, caller should return to main-loop
      // and handle input already.
      multiqueue_process_events(deferred_autocmd_events);
      return;
    }

    // TODO(bfredl): as an further micro-optimization, we could check whether
    // event.handler already checked input.
    os_breakcheck();
    if (input_available() || got_int) {
      return;
    }
  }
}

/// Return true if in the current mode we need to use virtual.
bool virtual_active(win_T *wp)
{
  // While an operator is being executed we return "virtual_op", because
  // VIsual_active has already been reset, thus we can't check for "block"
  // being used.
  if (virtual_op != kNone) {
    return virtual_op;
  }

  // In Terminal mode the cursor can be positioned anywhere by the application
  if (State & MODE_TERMINAL) {
    return true;

Review comment (suggestion):
@justinmk let me know if you were thinking of a different spot to process events in `state_handle_k_event`.

Code after:

    int execute_result = s->execute(s, key);
    if (!execute_result) {
      break;
    } else if (execute_result == -1) {
      goto getkey;
    }
  }
}

/// process events on main_loop, but interrupt if input is available
///
/// This should be used to handle K_EVENT in states accepting input
/// otherwise bursts of events can block break checking indefinitely.
void state_handle_k_event(void)
{
  while (true) {
    Event event = multiqueue_get(main_loop.events);
    if (event.handler) {
      event.handler(event.argv);
    }

    if (multiqueue_empty(main_loop.events)) {
      // don't breakcheck before return, caller should return to main-loop
      // and handle input already.
      return;
    }

    // TODO(bfredl): as an further micro-optimization, we could check whether
    // event.handler already checked input.
    os_breakcheck();
    if (input_available() || got_int) {
      return;
    }
  }
}

/// Return true if in the current mode we need to use virtual.
bool virtual_active(win_T *wp)
{
  // While an operator is being executed we return "virtual_op", because
  // VIsual_active has already been reset, thus we can't check for "block"
  // being used.
  if (virtual_op != kNone) {
    return virtual_op;
  }

  // In Terminal mode the cursor can be positioned anywhere by the application
  if (State & MODE_TERMINAL) {
    return true;
  }
Repository: neovim/neovim
File: test/functional/autocmd/markset_spec.lua (Lua)

Code before:
local t = require('test.testutil')
local n = require('test.functional.testnvim')()

local api = n.api
local clear = n.clear
local command = n.command
local feed = n.feed
local poke_eventloop = n.poke_eventloop
local eval = n.eval

local eq = t.eq

describe('MarkSet', function()
  before_each(function()
    clear()
    command("autocmd MarkSet * let g:autocmd ..= 'M'")
    command([[let g:autocmd = '']])
    api.nvim_buf_set_lines(0, 0, -1, true, {
      'foo\0bar',
      'baz text',
    })
  end)

  it('is called after a lowercase mark is set', function()
    feed('ma')
    poke_eventloop()
    feed('j')
    poke_eventloop()
    feed('mb')
    poke_eventloop()

    eq('MM', eval('g:autocmd'))
  end)

  it('is called after an uppercase mark is set', function()
    feed('mA')
    poke_eventloop()
    feed('l')
    poke_eventloop()

Review comment (suggestion):
so far the tests are working? that is cool.

try adding a test that does insane things in the `MarkSet`, such as

- `:bwipeout!` on its buffer
- switches windows
- switches tabpage

and then the test accesses/asserts the `data` and other event state. 

Code after:
local t = require('test.testutil')
local n = require('test.functional.testnvim')()

local api = n.api
local clear = n.clear
local command = n.command
local feed = n.feed
local poke_eventloop = n.poke_eventloop
local eval = n.eval

local eq = t.eq
local neq = t.neq

describe('MarkSet', function()
  -- TODO(justinmk): support other marks?: [, ] <, > . ^ " '

  before_each(function()
    clear()
  end)

  it('emits when lowercase/uppercase/[/] marks are set', function()
    command([[
      let g:mark_names = ''
      let g:mark_events = []
      autocmd MarkSet * call add(g:mark_events, {'event': deepcopy(v:event)}) | let g:mark_names ..= expand('<amatch>')
      " TODO: there is a bug lurking here.
      " autocmd MarkSet * let g:mark_names ..= expand('<amatch>')
    ]])

    api.nvim_buf_set_lines(0, 0, -1, true, {
      'foo\0bar',
      'baz text',
      'line 3',
    })

    feed('ma')
    feed('j')
    command('mark b')
Repository: neovim/neovim
File: test/functional/autocmd/markset_spec.lua (Lua)

Code before:
      'baz text',
    })
  end)

  it('is called after a lowercase mark is set', function()
    feed('ma')
    poke_eventloop()
    feed('j')
    poke_eventloop()
    feed('mb')
    poke_eventloop()

    eq('MM', eval('g:autocmd'))
  end)

  it('is called after an uppercase mark is set', function()
    feed('mA')
    poke_eventloop()
    feed('l')
    poke_eventloop()
    feed('mB')
    poke_eventloop()
    feed('j')
    poke_eventloop()
    feed('mC')
    poke_eventloop()

    eq('MMM', eval('g:autocmd'))
  end)

  it('survives multiple operations: buffer wipeout, window/tab switching', function()
    local orig_bufnr = api.nvim_get_current_buf()

    command('enew')
    local second_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(second_bufnr, 0, -1, true, {
      'second buffer line 1',
      'second buffer line 2',
    })

    command('enew')
    local third_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(third_bufnr, 0, -1, true, {
      'third buffer line 1',
      'third buffer line 2',
    })

    -- Split windows
    command('split')
    command('vsplit')


Review comment (bug):
i don't think these `poke_eventloop` should be necessary? when `eval()` is called below, it should flush queued events. or if i'm wrong about that, maybe only one `poke_eventloop` is needed before that.

Code after:
    api.nvim_buf_set_lines(0, 0, -1, true, {
      'foo\0bar',
      'baz text',
      'line 3',
    })

    feed('ma')
    feed('j')
    command('mark b')

    poke_eventloop()
    eq('ab', eval('g:mark_names'))

    -- event-data is copied to `v:event`.
    eq({
      {
        event = {
          col = 0,
          line = 1,
          name = 'a',
        },
      },
      {
        event = {
          col = 0,
          line = 2,
          name = 'b',
        },
      },
    }, eval('g:mark_events'))

    feed('mA')
    feed('l')
    feed('mB')
    feed('j')
    feed('mC')

    feed('x') -- TODO(justinmk): Sets [,] marks but does not emit MarkSet event (yet).
    feed('0vll<esc>') -- TODO(justinmk): Sets <,> marks but does not emit MarkSet event (yet).
    -- XXX: set these marks manually to exercise these cases.
    api.nvim_buf_set_mark(0, '[', 2, 0, {})
    api.nvim_buf_set_mark(0, ']', 2, 0, {})
    api.nvim_buf_set_mark(0, '<', 2, 0, {})
    api.nvim_buf_set_mark(0, '>', 2, 0, {})
    api.nvim_buf_set_mark(0, '"', 2, 0, {})

    poke_eventloop()
    eq('abABC[]<>"', eval('g:mark_names'))
  end)

  it('can subscribe to specific marks by pattern', function()
Repository: neovim/neovim
File: test/functional/autocmd/markset_spec.lua (Lua)

Code before:
    })

    -- Split windows
    command('split')
    command('vsplit')

    -- Create new tabpage
    command('tabnew')
    command('split')

    -- Enhanced autocmd to capture more data
    command([[
      let g:markset_events = []
      autocmd MarkSet * call add(g:markset_events, {'bufnr': bufnr('%'), 'event': deepcopy(v:event)})
    ]])

    -- Go back to original buffer and set a mark
    command('buffer ' .. orig_bufnr)
    feed('gg')
    poke_eventloop()
    feed('mA')
    poke_eventloop()

    -- OPERATION 1: Switch to different window
    command('wincmd w')
    poke_eventloop()

    -- OPERATION 2: Switch to different tabpage
    command('tabnext')
    poke_eventloop()

    -- OPERATION 3: Set mark in current buffer
    feed('mB')
    poke_eventloop()

    -- OPERATION 4: Wipeout the original buffer (this is the nuclear option!)
    command('bwipeout! ' .. orig_bufnr)
    poke_eventloop()

    -- OPERATION 5: Switch windows while buffer is gone
    command('wincmd w')
    poke_eventloop()

    -- OPERATION 6: Create yet another buffer and set mark
    command('enew')
    local final_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(final_bufnr, 0, -1, true, {
      'final buffer after chaos',
      'line 2 of final buffer',
    })
    feed('j')

Review comment (suggestion):
`command()` is blocking/synchronous (i.e. it already does a "poke") so shouldn't need `poke_eventloop` . 

most of the `poke_eventloop` calls in this PR shouldn't be needed.

Code after:

    poke_eventloop()
    eq('abABC[]<>"', eval('g:mark_names'))
  end)

  it('can subscribe to specific marks by pattern', function()
    command([[
      let g:mark_names = ''
      autocmd MarkSet [ab] let g:mark_names ..= expand('<amatch>')
    ]])

    api.nvim_buf_set_lines(0, 0, -1, true, {
      'foo\0bar',
      'baz text',
    })

    feed('md')
    feed('mc')
    feed('l')
    feed('mb')
    feed('j')
    feed('ma')

    poke_eventloop()
    eq('ba', eval('g:mark_names'))
  end)

  it('handles marks across multiple windows/buffers', function()
    local orig_bufnr = api.nvim_get_current_buf()

    command('enew')
    local second_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(second_bufnr, 0, -1, true, {
      'second buffer line 1',
      'second buffer line 2',
    })

    command('enew')
    local third_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(third_bufnr, 0, -1, true, {
      'third buffer line 1',
      'third buffer line 2',
    })

    command('split')
    command('vsplit')

    command('tabnew')
    command('split')

    command([[
Repository: neovim/neovim
File: test/functional/autocmd/markset_spec.lua (Lua)

Code before:
    command([[
      let g:markset_events = []
      autocmd MarkSet * call add(g:markset_events, {'bufnr': bufnr('%'), 'event': deepcopy(v:event)})
    ]])

    -- Go back to original buffer and set a mark
    command('buffer ' .. orig_bufnr)
    feed('gg')
    poke_eventloop()
    feed('mA')
    poke_eventloop()

    -- OPERATION 1: Switch to different window
    command('wincmd w')
    poke_eventloop()

    -- OPERATION 2: Switch to different tabpage
    command('tabnext')
    poke_eventloop()

    -- OPERATION 3: Set mark in current buffer
    feed('mB')
    poke_eventloop()

    -- OPERATION 4: Wipeout the original buffer (this is the nuclear option!)
    command('bwipeout! ' .. orig_bufnr)
    poke_eventloop()

    -- OPERATION 5: Switch windows while buffer is gone
    command('wincmd w')
    poke_eventloop()

    -- OPERATION 6: Create yet another buffer and set mark
    command('enew')
    local final_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(final_bufnr, 0, -1, true, {
      'final buffer after chaos',
      'line 2 of final buffer',
    })
    feed('j')
    poke_eventloop()
    feed('mC')
    poke_eventloop()

    -- OPERATION 7: Close current tabpage
    command('tabclose')
    poke_eventloop()

    -- OPERATION 8: Set one more mark after tabpage closure
    feed('mD')
    poke_eventloop()

Review comment (bug):
The "crazy" things that we want to test are when the autocmd handler *itself* does a bdelete/bwipeout/window-switch on *its own window*. That is historically the source of many crashes in vim/nvim, and is one reason why "deferring" is valuable.

Code after:
    api.nvim_buf_set_lines(0, 0, -1, true, {
      'foo\0bar',
      'baz text',
    })

    feed('md')
    feed('mc')
    feed('l')
    feed('mb')
    feed('j')
    feed('ma')

    poke_eventloop()
    eq('ba', eval('g:mark_names'))
  end)

  it('handles marks across multiple windows/buffers', function()
    local orig_bufnr = api.nvim_get_current_buf()

    command('enew')
    local second_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(second_bufnr, 0, -1, true, {
      'second buffer line 1',
      'second buffer line 2',
    })

    command('enew')
    local third_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(third_bufnr, 0, -1, true, {
      'third buffer line 1',
      'third buffer line 2',
    })

    command('split')
    command('vsplit')

    command('tabnew')
    command('split')

    command([[
      let g:markset_events = []
      autocmd MarkSet * call add(g:markset_events, { 'buf': 0 + expand('<abuf>'), 'event': deepcopy(v:event) })
    ]])

    command('buffer ' .. orig_bufnr)
    feed('gg')
    feed('mA')

    command('wincmd w')
    command('tabnext')
Repository: neovim/neovim
File: src/nvim/autocmd.c (C)

Code before:
  evdata->eap = eap;
  if (data) {
    evdata->data = xmalloc(sizeof(Object));
    *evdata->data = copy_object(*data, NULL);
  } else {
    evdata->data = NULL;
  }

  multiqueue_put(deferred_autocmd_events, deferred_event, evdata);
}

/// Execute a deferred autocommand event
static void deferred_event(void **argv)
{
  DeferredEvent *e = argv[0];
  event_T event = e->event;
  char *fname = e->fname;
  char *fname_io = e->fname_io;
  int group = e->group;
  exarg_T *eap = e->eap;
  Object *data = e->data;

  Error err = ERROR_INIT;
  buf_T *buf = find_buffer_by_handle(e->buf, &err);
  if (buf) {
    // set v:event with event data (if any)
    save_v_event_T save_v_event;
    dict_T *v_event = get_v_event(&save_v_event);
    if (data && data->type == kObjectTypeDict) {
      for (size_t i = 0; i < data->data.dict.size; i++) {
        KeyValuePair item = data->data.dict.items[i];
        typval_T tv;
        object_to_vim(item.value, &tv, &err);
        if (ERROR_SET(&err)) {
          api_clear_error(&err);
          continue;
        }
        tv_dict_add_tv(v_event, item.key.data, item.key.size, &tv);
        tv_clear(&tv);
      }
    }
    tv_dict_set_keys_readonly(v_event);

    apply_autocmds_group(event, fname, fname_io, false, group, buf, eap, data);
    restore_v_event(v_event, &save_v_event);
  }

  if (data) {
    api_free_object(*data);
    xfree(data);
  }

Review comment (suggestion):
```suggestion
    // Copy `data` to `v:event`.
```

Code after:
  evdata->eap = eap;
  if (data) {
    evdata->data = xmalloc(sizeof(Object));
    *evdata->data = copy_object(*data, NULL);
  } else {
    evdata->data = NULL;
  }

  multiqueue_put(deferred_events, deferred_event, evdata);
}

/// Executes a deferred autocommand event.
static void deferred_event(void **argv)
{
  AutoCmdEvent *e = argv[0];
  event_T event = e->event;
  char *fname = e->fname;
  char *fname_io = e->fname_io;
  int group = e->group;
  exarg_T *eap = e->eap;
  Object *data = e->data;

  Error err = ERROR_INIT;
  buf_T *buf = find_buffer_by_handle(e->buf, &err);
  if (buf) {
    // Copy `data` to `v:event`.
    save_v_event_T save_v_event;
    dict_T *v_event = get_v_event(&save_v_event);
    if (data && data->type == kObjectTypeDict) {
      for (size_t i = 0; i < data->data.dict.size; i++) {
        KeyValuePair item = data->data.dict.items[i];
        typval_T tv;
        object_to_vim(item.value, &tv, &err);
        if (ERROR_SET(&err)) {
          api_clear_error(&err);
          continue;
        }
        tv_dict_add_tv(v_event, item.key.data, item.key.size, &tv);
        tv_clear(&tv);
      }
    }
    tv_dict_set_keys_readonly(v_event);

    aco_save_T aco;
    aucmd_prepbuf(&aco, buf);
    apply_autocmds_group(event, fname, fname_io, false, group, buf, eap, data);
    aucmd_restbuf(&aco);

    restore_v_event(v_event, &save_v_event);
  }
Repository: neovim/neovim
File: src/nvim/autocmd_defs.h (C/C++)

Code before:
  AutoPat *lastpat;         ///< Last matched AutoPat
  size_t auidx;             ///< Current autocmd index to execute
  size_t ausize;            ///< Saved AutoCmd vector size
  char *afile_orig;         ///< Unexpanded <afile>
  char *fname;              ///< Fname to match with
  char *sfname;             ///< Sfname to match with
  char *tail;               ///< Tail of fname
  int group;                ///< Group being used
  event_T event;            ///< Current event
  sctx_T script_ctx;        ///< Script context where it is defined
  int arg_bufnr;            ///< Initially equal to <abuf>, set to zero when buf is deleted
  Object *data;             ///< Arbitrary data
  AutoPatCmd *next;         ///< Chain of active apc-s for auto-invalidation
};

typedef kvec_t(AutoCmd) AutoCmdVec;

typedef struct {
  event_T event;
  char *fname;
  char *fname_io;
  Buffer buf;
  int group;
  exarg_T *eap;
  Object *data;
} DeferredEvent;


Review comment (suggestion):
```suggestion
} AutoCmdEvent;  // Used for "deferred" events, but can represent any event.
```

Code after:
  AutoPat *lastpat;         ///< Last matched AutoPat
  size_t auidx;             ///< Current autocmd index to execute
  size_t ausize;            ///< Saved AutoCmd vector size
  char *afile_orig;         ///< Unexpanded <afile>
  char *fname;              ///< Fname to match with
  char *sfname;             ///< Sfname to match with
  char *tail;               ///< Tail of fname
  int group;                ///< Group being used
  event_T event;            ///< Current event
  sctx_T script_ctx;        ///< Script context where it is defined
  int arg_bufnr;            ///< Initially equal to <abuf>, set to zero when buf is deleted
  Object *data;             ///< Arbitrary data
  AutoPatCmd *next;         ///< Chain of active apc-s for auto-invalidation
};

typedef kvec_t(AutoCmd) AutoCmdVec;

typedef struct {
  event_T event;
  char *fname;
  char *fname_io;
  Buffer buf;
  int group;
  exarg_T *eap;
  Object *data;
} AutoCmdEvent;  // Used for "deferred" events, but can represent any event.
Repository: neovim/neovim
File: test/functional/autocmd/markset_spec.lua (Lua)

Code before:
local function get_event(all_events, mark)
  return vim.iter(all_events):find(function(event)
    return event.event.name == mark
  end)
end

describe('MarkSet', function()
  before_each(function()
    clear()
  end)

  it('is called after a lowercase mark is set', function()
    command("autocmd MarkSet * let g:autocmd ..= 'M'")
    command([[let g:autocmd = '']])
    api.nvim_buf_set_lines(0, 0, -1, true, {
      'foo\0bar',
      'baz text',
    })

    feed('ma')
    feed('j')
    feed('mb')

    poke_eventloop()

    eq('MM', eval('g:autocmd'))
  end)

  it('is called after an uppercase mark is set', function()
    command("autocmd MarkSet * let g:autocmd ..= 'M'")
    command([[let g:autocmd = '']])
    api.nvim_buf_set_lines(0, 0, -1, true, {
      'foo\0bar',
      'baz text',
    })

    feed('mA')
    feed('l')
    feed('mB')
    feed('j')
    feed('mC')

    poke_eventloop()

    eq('MMM', eval('g:autocmd'))
  end)

  it('can only be called for a specific pattern (mark)', function()
    command("autocmd MarkSet a let g:autocmd ..= 'M'")
    command([[let g:autocmd = '']])
    api.nvim_buf_set_lines(0, 0, -1, true, {

Review comment (suggestion):
Interesting that `poke_eventloop` is needed here. I guess because of the "defer". 

Maybe we should consider flushing `deferred_events` when a request happens... 



Code after:
    clear()
  end)

  it('emits when lowercase/uppercase/[/] marks are set', function()
    command([[
      let g:mark_names = ''
      let g:mark_events = []
      autocmd MarkSet * call add(g:mark_events, {'event': deepcopy(v:event)}) | let g:mark_names ..= expand('<amatch>')
      " TODO: there is a bug lurking here.
      " autocmd MarkSet * let g:mark_names ..= expand('<amatch>')
    ]])

    api.nvim_buf_set_lines(0, 0, -1, true, {
      'foo\0bar',
      'baz text',
      'line 3',
    })

    feed('ma')
    feed('j')
    command('mark b')

    poke_eventloop()
    eq('ab', eval('g:mark_names'))

    -- event-data is copied to `v:event`.
    eq({
      {
        event = {
          col = 0,
          line = 1,
          name = 'a',
        },
      },
      {
        event = {
          col = 0,
          line = 2,
          name = 'b',
        },
      },
    }, eval('g:mark_events'))

    feed('mA')
    feed('l')
    feed('mB')
    feed('j')
    feed('mC')

    feed('x') -- TODO(justinmk): Sets [,] marks but does not emit MarkSet event (yet).
    feed('0vll<esc>') -- TODO(justinmk): Sets <,> marks but does not emit MarkSet event (yet).
Repository: neovim/neovim
File: test/functional/autocmd/markset_spec.lua (Lua)

Code before:
    feed('mB')

    command('wincmd w')
    command('enew')

    local final_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(final_bufnr, 0, -1, true, {
      'final buffer after chaos',
      'line 2 of final buffer',
    })

    feed('j')
    feed('mC')

    command('tabclose')

    feed('mD')
    poke_eventloop()

    local mark_d_bufnr = api.nvim_get_current_buf()
    local all_events = eval('g:markset_events')
    eq(4, #all_events, 'Should have at 4 MarkSet events')

    local mark_a = get_event(all_events, 'A')
    eq(orig_bufnr, mark_a.bufnr)
    eq('A', mark_a.event.name)
    eq(1, mark_a.event.line)
    eq(0, mark_a.event.col)

    local mark_b = get_event(all_events, 'B')
    eq(third_bufnr, mark_b.bufnr)
    eq('B', mark_b.event.name)
    eq(1, mark_b.event.line)
    eq(0, mark_b.event.col)

    local mark_c = get_event(all_events, 'C')
    eq(final_bufnr, mark_c.bufnr)
    eq('C', mark_c.event.name)
    eq(2, mark_c.event.line)
    eq(0, mark_c.event.col)

    local mark_d = get_event(all_events, 'D')
    eq(mark_d_bufnr, mark_d.bufnr)
    eq('D', mark_d.event.name)
    eq(1, mark_d.event.line)
    eq(0, mark_d.event.col)
  end)

  it('handles an autocommand that calls bwipeout!', function()
    api.nvim_buf_set_lines(0, 0, -1, true, {
      'line 1',

Review comment (suggestion):
Just test the entire thing with `eq(..., all_events)`. More readable, more meaningful, and less code.

Code after:
    command('wincmd w')
    command('enew')

    local final_bufnr = api.nvim_get_current_buf()
    api.nvim_buf_set_lines(final_bufnr, 0, -1, true, {
      'final buffer after chaos',
      'line 2 of final buffer',
    })

    feed('j')
    feed('mC')

    command('tabclose')

    feed('mD')

    poke_eventloop()
    eq({
      {
        buf = 1,
        event = {
          col = 0,
          line = 1,
          name = 'A',
        },
      },
      {
        buf = 2,
        event = {
          col = 0,
          line = 1,
          name = 'B',
        },
      },
      {
        buf = 4,
        event = {
          col = 0,
          line = 2,
          name = 'C',
        },
      },
      {
        buf = 3,
        event = {
          col = 0,
          line = 1,
          name = 'D',
        },
      },
    }, eval('g:markset_events'))