Repository: neovim/neovim
File: src/nvim/api/vim.c (C)

Code before:
  if (HAS_KEY(opts, open_term, on_input)) {
    cb = opts->on_input;
    opts->on_input = LUA_NOREF;
  }

  Channel *chan = channel_alloc(kChannelStreamInternal);
  chan->stream.internal.cb = cb;
  chan->stream.internal.closed = false;
  TerminalOptions topts = {
    .data = chan,
    // NB: overridden in terminal_check_size if a window is already
    // displaying the buffer
    .width = (uint16_t)MAX(curwin->w_view_width - win_col_off(curwin), 0),
    .height = (uint16_t)curwin->w_view_height,
    .write_cb = term_write,
    .resize_cb = term_resize,
    .close_cb = term_close,
    .force_crlf = GET_BOOL_OR_TRUE(opts, open_term, force_crlf),
  };

  // Read existing buffer contents (if any)
  StringBuilder contents = KV_INITIAL_VALUE;
  for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
    char *bufstr = ml_get_buf(buf, lnum);
    colnr_T bufstrlen = ml_get_buf_len(buf, lnum);
    kv_concat_len(contents, bufstr, (size_t)bufstrlen);
    if (lnum < buf->b_ml.ml_line_count) {
      kv_push(contents, '\n');
    }
  }

  channel_incref(chan);
  terminal_open(&chan->term, buf, topts);
  if (chan->term != NULL) {
    terminal_check_size(chan->term);
  }
  channel_decref(chan);

  // Write buffer contents to channel. channel_send takes ownership of the
  // buffer so we do not need to free it.
  if (contents.size > 0) {
    const char *error = NULL;
    channel_send(chan->id, contents.items, contents.size, true, &error);
    VALIDATE(!error, "%s", error, {});
  }

  return (Integer)chan->id;
}

static void term_write(const char *buf, size_t size, void *data)
{

Review comment (suggestion):
Translate NL to Nul when concatenating? And this shares some common code with `read_input()` in src/nvim/os/shell.c.

Code after:
  if (HAS_KEY(opts, open_term, on_input)) {
    cb = opts->on_input;
    opts->on_input = LUA_NOREF;
  }

  Channel *chan = channel_alloc(kChannelStreamInternal);
  chan->stream.internal.cb = cb;
  chan->stream.internal.closed = false;
  TerminalOptions topts = {
    .data = chan,
    // NB: overridden in terminal_check_size if a window is already
    // displaying the buffer
    .width = (uint16_t)MAX(curwin->w_view_width - win_col_off(curwin), 0),
    .height = (uint16_t)curwin->w_view_height,
    .write_cb = term_write,
    .resize_cb = term_resize,
    .close_cb = term_close,
    .force_crlf = GET_BOOL_OR_TRUE(opts, open_term, force_crlf),
  };

  // Read existing buffer contents (if any)
  StringBuilder contents = KV_INITIAL_VALUE;
  read_buffer_into(buf, 1, buf->b_ml.ml_line_count, &contents);

  channel_incref(chan);
  terminal_open(&chan->term, buf, topts);
  if (chan->term != NULL) {
    terminal_check_size(chan->term);
  }
  channel_decref(chan);

  // Write buffer contents to channel. channel_send takes ownership of the
  // buffer so we do not need to free it.
  if (contents.size > 0) {
    const char *error = NULL;
    channel_send(chan->id, contents.items, contents.size, true, &error);
    VALIDATE(!error, "%s", error, {});
  }

  return (Integer)chan->id;
}

static void term_write(const char *buf, size_t size, void *data)
{
  Channel *chan = data;
  LuaRef cb = chan->stream.internal.cb;
  if (cb == LUA_NOREF) {
    return;
  }
  MAXSIZE_TEMP_ARRAY(args, 3);
  ADD_C(args, INTEGER_OBJ((Integer)chan->id));