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

Code before:
      char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : "";
      char *req = option_has_scope(*opt_idxp, kOptScopeBuf)
                  ? "buffer-local "
                  : (option_has_scope(*opt_idxp, kOptScopeWin) ? "window-local " : "");

      api_set_error(err, kErrorTypeValidation, "'%s' cannot be passed for %s%soption '%s'",
                    tgt, global, req, name);
    }
  }

  return ERROR_SET(err) ? FAIL : OK;
#undef HAS_KEY_X
}

/// Create a dummy buffer and run the FileType autocmd on it.
static buf_T *do_ft_buf(const char *filetype, aco_save_T *aco, bool *aco_used, Error *err)
  FUNC_ATTR_NONNULL_ARG(2, 3, 4)
{
  *aco_used = false;
  if (filetype == NULL) {
    return NULL;
  }
  // Not firing nested FileType autocmds; bail early if one's running.
  if (in_filetype_autocmd()) {
    api_set_error(err, kErrorTypeException,
                  "Cannot detect default while FileType autocommands are running");
    return NULL;
  }

  // Allocate a buffer without putting it in the buffer list.
  buf_T *ftbuf = buflist_new(NULL, NULL, 1, BLN_DUMMY);
  if (ftbuf == NULL) {
    api_set_error(err, kErrorTypeException, "Could not create internal buffer");
    return NULL;
  }

  // Open a memline for use by autocommands.
  if (ml_open(ftbuf) == FAIL) {
    api_set_error(err, kErrorTypeException, "Could not load internal buffer");
    wipe_ft_buf(ftbuf);
    return NULL;
  }

  bufref_T bufref;
  set_bufref(&bufref, ftbuf);

  // Set curwin/curbuf to buf and save a few things.
  aucmd_prepbuf(aco, ftbuf);
  *aco_used = true;

  set_option_direct(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, SID_NONE);

Review comment (bug):
This issue bit me recently. `nvim_get_option_value` sometimes gave incorrect defaults from `LspAttach` when it indirectly ran from `FileType`...

Maybe some level of recursion can be allowed later, but for now the error is better than silence.

Code after:
                  ? "buffer-local "
                  : (option_has_scope(*opt_idxp, kOptScopeWin) ? "window-local " : "");

      api_set_error(err, kErrorTypeValidation, "'%s' cannot be passed for %s%soption '%s'",
                    tgt, global, req, name);
    }
  }

  return ERROR_SET(err) ? FAIL : OK;
#undef HAS_KEY_X
}

/// Create a dummy buffer and run the FileType autocmd on it.
static buf_T *do_ft_buf(const char *filetype, aco_save_T *aco, bool *aco_used, Error *err)
  FUNC_ATTR_NONNULL_ARG(2, 3, 4)
{
  *aco_used = false;
  if (filetype == NULL) {
    return NULL;
  }

  // Allocate a buffer without putting it in the buffer list.
  buf_T *ftbuf = buflist_new(NULL, NULL, 1, BLN_DUMMY);
  if (ftbuf == NULL) {
    api_set_error(err, kErrorTypeException, "Could not create internal buffer");
    return NULL;
  }

  // Open a memline for use by autocommands.
  if (ml_open(ftbuf) == FAIL) {
    api_set_error(err, kErrorTypeException, "Could not load internal buffer");
    return ftbuf;
  }

  bufref_T bufref;
  set_bufref(&bufref, ftbuf);

  // Set curwin/curbuf to buf and save a few things.
  aucmd_prepbuf(aco, ftbuf);
  *aco_used = true;

  set_option_direct(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, SID_NONE);
  set_option_direct(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, SID_NONE);
  assert(ftbuf->b_ml.ml_mfp->mf_fd < 0);  // ml_open() should not have opened swapfile already
  ftbuf->b_p_swf = false;
  ftbuf->b_p_ml = false;
  ftbuf->b_p_ft = xstrdup(filetype);

  TRY_WRAP(err, {
    do_filetype_autocmd(ftbuf, false);
  });